August 23rd, 2024

Goal

My goal for this week is to continue reorganizing the application folder structure, refining the codebase, and refactoring the code.

Hypothesis

If the application folder structure and codebase are reorganized in an orderly manner, the program will become more maintainable and navigable without causing significant disruptions or errors.

Expected Results

  1. Application folders will be more logically organized, making it easier to locate and manage files.
  2. Refactoring the code during reorganization will make the code cleaner, more readable, and more efficient.
  3. Restructuring the codebase will allow for better scalability, maintainability, and easier integration of new features.

Results

I have finished moving all the files into their folders and reduced the codebase by another 5,000 lines. Originally, the codebase had 47,978 lines; now, it has 35,483. The difference is 12,495 lines, about 26% of the original codebase. The final step to complete the organization would be to move all the source code files into the src folder. But this is a big undertaking, as it involves separating the source code from local files and reconfiguring the file paths through GlobalSettings. I plan to tackle this gradually.

I was able to document more instances of unwanted errors while using the app. One of them is the error:

Failed to find a valid link for ID: ####. Please make sure this ID is available in the selected database.

There's a mismatch between the fetched links displayed in the table and their availability for downloading. This is an issue that I will be looking at for my next steps.

Image 1:

NCBI Window

Image 2:

NCBI Window

Next Steps

Apart from continuing to refactor and reorganize the code, I plan to implement lazy initialization in the application. This means that resources (such as functions and classes) will only be created or initialized when needed. All the windows are initialized at once during startup, which could slow the program during usage. By implementing lazy initialization, the initialization time will be distributed throughout the program's usage, making delays more negligible.

I am also looking to implement a new feature in CASPER. The details of this feature are still being worked on. Once the concept is clear, I will begin experimenting before integrating it into CASPER. This is one of the key topics Dr. Trinh is looking for during our meeting this week.

The following are the remaining items on my agenda for application development:

Files changed (60) hide show
  1. CoTargeting.py +0 -289
  2. NewEndonuclease.py +0 -406
  3. closingWin.py +0 -197
  4. common_utils.py +0 -10
  5. controllers/CoTargeting.py +139 -0
  6. OffTarget.py → controllers/OffTarget.py +25 -234
  7. Results.py → controllers/Results.py +9 -8
  8. multitargeting.py → controllers/multitargeting.py +68 -701
  9. controllers/ncbi.py +915 -0
  10. populationAnalysis.py → controllers/populationAnalysis.py +76 -536
  11. scoring_window.py → controllers/scoring_window.py +3 -3
  12. error_handling.py +0 -21
  13. logs/CASPER.log +0 -0
  14. main.py +8 -1635
  15. CSPRparser.py → models/CSPRparser.py +1 -5
  16. GlobalSettings.py → models/GlobalSettings.py +0 -0
  17. ncbi.py +0 -1686
  18. linux.spec → scripts/linux.spec +0 -0
  19. mac.spec → scripts/mac.spec +0 -0
  20. windows.spec → scripts/windows.spec +0 -0
  21. windowsInstallerScript.iss → scripts/windowsInstallerScript.iss +0 -0
  22. CASPER_main.ui → ui/CASPER_main.ui +0 -0
  23. NewGenome.ui → ui/NewGenome.ui +0 -0
  24. annotation_details.ui → ui/annotation_details.ui +0 -0
  25. closing_window.ui → ui/closing_window.ui +0 -0
  26. cotargeting.ui → ui/cotargeting.ui +0 -0
  27. export_tool.ui → ui/export_tool.ui +0 -0
  28. filter_options.ui → ui/filter_options.ui +0 -0
  29. generate_library.ui → ui/generate_library.ui +0 -0
  30. loading.ui → ui/loading.ui +0 -0
  31. loading_data_form.ui → ui/loading_data_form.ui +0 -0
  32. mt.ui → ui/mt.ui +0 -0
  33. multitargeting_sql_settings.ui → ui/multitargeting_sql_settings.ui +0 -0
  34. multitargeting_stats.ui → ui/multitargeting_stats.ui +0 -0
  35. name_form.ui → ui/name_form.ui +0 -0
  36. ncbi.ui → ui/ncbi.ui +0 -0
  37. ncbi_nav_page.ui → ui/ncbi_nav_page.ui +0 -0
  38. ncbi_rename_window.ui → ui/ncbi_rename_window.ui +0 -0
  39. newendonuclease.ui → ui/newendonuclease.ui +0 -0
  40. newgenomenavigationpage.ui → ui/newgenomenavigationpage.ui +0 -0
  41. off_target.ui → ui/off_target.ui +0 -0
  42. pop.ui → ui/pop.ui +0 -0
  43. results.ui → ui/results.ui +0 -0
  44. scoring_window.ui → ui/scoring_window.ui +0 -0
  45. startupCASPER.ui → ui/startupCASPER.ui +0 -0
  46. Algorithms.py → utils/Algorithms.py +1 -1
  47. ui_utils.py → utils/ui.py +44 -10
  48. utils/web.py +17 -0
  49. AnnotationParser.py → views/AnnotationParser.py +1 -6
  50. views/AnnotationWindow.py +125 -0
  51. main.py → views/CMainWindow.py +62 -780
  52. GenBankParse.py → views/GenBankParse.py +0 -0
  53. views/NewEndonuclease.py +228 -0
  54. NewGenome.py → views/NewGenome.py +55 -433
  55. views/StartupWindow.py +255 -0
  56. annotation_functions.py → views/annotation_functions.py +0 -0
  57. views/closingWin.py +73 -0
  58. export_tool.py → views/export_tool.py +22 -184
  59. generateLib.py → views/generateLib.py +95 -369
  60. genomeBrowser.py → views/genomeBrowser.py +9 -53
CoTargeting.py DELETED
@@ -1,289 +0,0 @@
1
- from PyQt5 import QtWidgets, Qt, QtGui, QtCore, uic
2
- import GlobalSettings
3
- import traceback
4
- import math
5
-
6
- #global logger
7
- logger = GlobalSettings.logger
8
-
9
- ######################################################
10
- # CoTargeting class: This class is a window that lets the user select which endonucleases to co-target with
11
- # inputs are from the user and from results
12
- # from results: the organism name and the list of endonucleases for that organism
13
- # from user: which endonucleases to co-target
14
- ######################################################
15
- class CoTargeting(QtWidgets.QMainWindow):
16
-
17
- def __init__(self, path):
18
- try:
19
- # pyqt stuff
20
- super(CoTargeting, self).__init__()
21
- uic.loadUi(GlobalSettings.appdir + 'cotargeting.ui', self)
22
- self.setWindowIcon(QtGui.QIcon(GlobalSettings.appdir + "cas9image.ico"))
23
- self.setWindowTitle("Co-targeting")
24
-
25
- # endo_table stuff
26
- self.endo_table.setColumnCount(1) # hardcoded because there will always be 1 columns
27
- self.endo_table.setShowGrid(True)
28
- self.endo_table.setHorizontalHeaderLabels("Endonuclease;".split(";"))
29
- self.endo_table.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
30
- self.endo_table.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
31
- self.endo_table.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
32
- self.endo_table.horizontalHeader().setSectionResizeMode(0, QtWidgets.QHeaderView.Stretch) #Ensures last column goes to the edge of table
33
-
34
- # variables
35
- self.info_path = path
36
-
37
- # button connections
38
- self.cancel_button.clicked.connect(self.cancel_function)
39
- self.submit_button.clicked.connect(self.submission_function)
40
-
41
- #scale UI
42
- self.scaleUI()
43
-
44
- except Exception as e:
45
- logger.critical("Error initializing CoTargeting class.")
46
- logger.critical(e)
47
- logger.critical(traceback.format_exc())
48
- msgBox = QtWidgets.QMessageBox()
49
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
50
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
51
- msgBox.setWindowTitle("Fatal Error")
52
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
53
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
54
- msgBox.exec()
55
-
56
- exit(-1)
57
-
58
- #scale UI based on current screen
59
- def scaleUI(self):
60
- try:
61
- self.repaint()
62
- QtWidgets.QApplication.processEvents()
63
-
64
- screen = self.screen()
65
- dpi = screen.physicalDotsPerInch()
66
- width = screen.geometry().width()
67
- height = screen.geometry().height()
68
-
69
- # font scaling
70
- fontSize = 12
71
- self.fontSize = fontSize
72
- self.centralWidget().setStyleSheet("font: " + str(fontSize) + "pt 'Arial';")
73
-
74
- # CASPER header scaling
75
- fontSize = 20
76
- self.title.setStyleSheet("font: bold " + str(fontSize) + "pt 'Arial';")
77
-
78
- self.adjustSize()
79
-
80
- currentWidth = self.size().width()
81
- currentHeight = self.size().height()
82
-
83
- # window scaling
84
- # 1920x1080 => 850x750
85
- scaledWidth = int((width * 450) / 1920)
86
- scaledHeight = int((height * 375) / 1080)
87
-
88
- if scaledHeight < currentHeight:
89
- scaledHeight = currentHeight
90
- if scaledWidth < currentWidth:
91
- scaledWidth = currentWidth
92
-
93
- screen = QtWidgets.QApplication.desktop().screenNumber(QtWidgets.QApplication.desktop().cursor().pos())
94
- centerPoint = QtWidgets.QApplication.desktop().screenGeometry(screen).center()
95
- x = centerPoint.x()
96
- y = centerPoint.y()
97
- x = x - (math.ceil(scaledWidth / 2))
98
- y = y - (math.ceil(scaledHeight / 2))
99
- self.setGeometry(x, y, scaledWidth, scaledHeight)
100
-
101
- self.repaint()
102
- QtWidgets.QApplication.processEvents()
103
- except Exception as e:
104
- logger.critical("Error in scaleUI() in cotargeting in results.")
105
- logger.critical(e)
106
- logger.critical(traceback.format_exc())
107
- msgBox = QtWidgets.QMessageBox()
108
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
109
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
110
- msgBox.setWindowTitle("Fatal Error")
111
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
112
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
113
- msgBox.exec()
114
-
115
- exit(-1)
116
-
117
- #center UI on current screen
118
- def centerUI(self):
119
- try:
120
- self.repaint()
121
- QtWidgets.QApplication.processEvents()
122
-
123
- # center window on current screen
124
- width = self.width()
125
- height = self.height()
126
- screen = QtWidgets.QApplication.desktop().screenNumber(QtWidgets.QApplication.desktop().cursor().pos())
127
- centerPoint = QtWidgets.QApplication.desktop().screenGeometry(screen).center()
128
- x = centerPoint.x()
129
- y = centerPoint.y()
130
- x = x - (math.ceil(width / 2))
131
- y = y - (math.ceil(height / 2))
132
- self.setGeometry(x, y, width, height)
133
-
134
- self.repaint()
135
- QtWidgets.QApplication.processEvents()
136
- except Exception as e:
137
- logger.critical("Error in centerUI() in cotargeting in results.")
138
- logger.critical(e)
139
- logger.critical(traceback.format_exc())
140
- msgBox = QtWidgets.QMessageBox()
141
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
142
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
143
- msgBox.setWindowTitle("Fatal Error")
144
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
145
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
146
- msgBox.exec()
147
-
148
- exit(-1)
149
-
150
- # launches the window
151
- # it is expecting endo_choices in the form of a list, and the orgName in the form of a string
152
- # it sets the organism name, and sets the table as well
153
- def launch(self, endo_choices, orgName):
154
- try:
155
- self.orgName.setText(orgName)
156
- setTableList = list()
157
- # only get the endo choices that were original
158
- for item in endo_choices:
159
- checkList = item.split(",")
160
- if len(checkList) == 1 and "|" not in item: #Prevent cotarget endos from being added back in
161
- setTableList.append(item)
162
-
163
- # go through and set each table item, but also set the row count
164
- self.endo_table.setRowCount(len(setTableList))
165
- loopCount = 0
166
- for item in setTableList:
167
- tabWidget = QtWidgets.QTableWidgetItem(item)
168
- self.endo_table.setItem(loopCount, 0, tabWidget)
169
- loopCount += 1
170
- self.endo_table.resizeColumnsToContents()
171
-
172
- # now show
173
- self.centerUI()
174
- self.show()
175
- self.activateWindow()
176
- except Exception as e:
177
- logger.critical("Error in launch() in CoTargeting.")
178
- logger.critical(e)
179
- logger.critical(traceback.format_exc())
180
- msgBox = QtWidgets.QMessageBox()
181
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
182
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
183
- msgBox.setWindowTitle("Fatal Error")
184
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
185
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
186
- msgBox.exec()
187
-
188
- exit(-1)
189
-
190
- # this is the cancel function
191
- # it clears the table, sets the organism to nothing, and hides the window
192
- def cancel_function(self):
193
- try:
194
- self.endo_table.clearContents()
195
- self.endo_table.setRowCount(0)
196
- self.orgName.setText("")
197
- self.hide()
198
- except Exception as e:
199
- logger.critical("Error in cancel_function() in CoTargeting.")
200
- logger.critical(e)
201
- logger.critical(traceback.format_exc())
202
- msgBox = QtWidgets.QMessageBox()
203
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
204
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
205
- msgBox.setWindowTitle("Fatal Error")
206
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
207
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
208
- msgBox.exec()
209
-
210
- exit(-1)
211
-
212
- # this is the submission function.
213
- # it makes sure the user selects at least 2 endonucleases
214
- # then it goes through and returns the endonucleases selected
215
- # once it gets those, it then calls a function in Results that repopulates the table correctly
216
- def submission_function(self):
217
- try:
218
- #get endo data from CASPERinfo
219
- self.Endos = {}
220
- f = open(GlobalSettings.appdir + 'CASPERinfo')
221
- while True:
222
- line = f.readline()
223
- if line.startswith('ENDONUCLEASES'):
224
- while True:
225
- line = f.readline()
226
- if (line[0] == "-"):
227
- break
228
- line_tokened = line.split(";")
229
- endo = line_tokened[0]
230
- self.Endos[endo] = ([line_tokened[2], line_tokened[3], line_tokened[4]],line_tokened[5])
231
- break
232
- f.close()
233
-
234
- # set the selected_list, and make sure they select at least 2 endonucleases
235
- selected_list = self.endo_table.selectedItems()
236
- if len(selected_list) <= 1:
237
- msgBox = QtWidgets.QMessageBox()
238
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
239
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
240
- msgBox.setWindowTitle("Nothing Selected")
241
- msgBox.setText(
242
- "No endonucleases selected. Please select at least 2 endonucleases")
243
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
244
- msgBox.exec()
245
-
246
- return
247
-
248
- # go through and get which endonuclease's have been selected
249
- ret_endo_list = list()
250
- for i in range(self.endo_table.rowCount()):
251
- if self.endo_table.item(i, 0).isSelected():
252
- ret_endo_list.append(self.endo_table.item(i, 0).text())
253
-
254
- #invalid_flag = False
255
- for endo1 in ret_endo_list:
256
- for endo2 in ret_endo_list:
257
- if endo1 == endo2:
258
- continue
259
- endo1_len = sum([int(x) for x in self.Endos[endo1][0]])
260
- endo2_len = sum([int(x) for x in self.Endos[endo2][0]])
261
- if endo1_len != endo2_len or self.Endos[endo1][1] != self.Endos[endo2][1]: # If endonucleases don't have the same length gRNA or don't have the same directionality, throw an error
262
- msgBox = QtWidgets.QMessageBox()
263
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
264
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
265
- msgBox.setWindowTitle("Invalid Endonucleases")
266
- msgBox.setText(
267
- "The selected endonucleases are not compatible.")
268
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
269
- msgBox.exec()
270
-
271
- return
272
-
273
- GlobalSettings.mainWindow.Results.co_target_endo_list = ret_endo_list
274
- GlobalSettings.mainWindow.Results.populate_cotarget_table()
275
- self.cancel_function()
276
- except Exception as e:
277
- logger.critical("Error in submission_function() in CoTargeting.")
278
- logger.critical(e)
279
- logger.critical(traceback.format_exc())
280
- msgBox = QtWidgets.QMessageBox()
281
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
282
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
283
- msgBox.setWindowTitle("Fatal Error")
284
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
285
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
286
- msgBox.exec()
287
-
288
- exit(-1)
289
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
NewEndonuclease.py DELETED
@@ -1,406 +0,0 @@
1
- import sys, os
2
- from PyQt5 import QtWidgets, uic, QtGui, QtCore, Qt
3
- import GlobalSettings
4
- from PyQt5.QtGui import QIntValidator
5
- import traceback
6
- import math
7
-
8
- #global logger
9
- logger = GlobalSettings.logger
10
-
11
- class NewEndonuclease(QtWidgets.QMainWindow):
12
-
13
- def __init__(self):
14
- try:
15
- super(NewEndonuclease, self).__init__()
16
- uic.loadUi(GlobalSettings.appdir + 'newendonuclease.ui', self)
17
- self.setWindowIcon(Qt.QIcon(GlobalSettings.appdir + "cas9image.ico"))
18
- self.setWindowTitle('New Endonuclease')
19
- self.error = False
20
- pamFlag = False
21
-
22
- self.onList = []
23
- self.offList = []
24
-
25
- self.onList, self.offList = self.get_on_off_data() ### Call function to fill on- and off- data name lists
26
-
27
- for name in self.onList: ### Add on-target names to drop-down
28
- self.comboBox.addItem(str(name))
29
-
30
- for name in self.offList: ### Add off-target names to drop-down
31
- self.comboBox_2.addItem(str(name))
32
-
33
-
34
- self.submit_button.clicked.connect(self.submit)
35
- self.cancel_button.clicked.connect(self.cancel)
36
-
37
-
38
- ### Set up validators for input fields:
39
- reg_ex1 = QtCore.QRegExp("[^/\\\\_]+") # No slashes or underscores
40
- reg_ex2 = QtCore.QRegExp("[^/\\\\_\\s]+") # No slashes, underscores, or spaces
41
- reg_ex3 = QtCore.QRegExp("[acdefghiklmnpqrstvwyACDEFGHIKLMNPQRSTVWY\S]+") # Only approved PAM characters and no spaces
42
- input_validator1 = QtGui.QRegExpValidator(reg_ex1, self)
43
- input_validator2 = QtGui.QRegExpValidator(reg_ex2, self)
44
- input_validator3 = QtGui.QRegExpValidator(reg_ex3, self)
45
- self.organism_name.setValidator(input_validator1)
46
- self.abbreviation.setValidator(input_validator2)
47
- self.pam_sequence.setValidator(input_validator3)
48
-
49
-
50
- self.seed_length.setValidator(QIntValidator(0,30,self.seed_length))
51
- self.five_length.setValidator(QIntValidator(0,20,self.five_length))
52
- self.three_length.setValidator(QIntValidator(0,20,self.three_length))
53
-
54
- groupbox_style = """
55
- QGroupBox:title{subcontrol-origin: margin;
56
- left: 10px;
57
- padding: 0 5px 0 5px;}
58
- QGroupBox#groupBox{border: 2px solid rgb(111,181,110);
59
- border-radius: 9px;
60
- font: bold 14pt 'Arial';
61
- margin-top: 10px;}"""
62
-
63
- self.groupBox.setStyleSheet(groupbox_style)
64
- self.groupBox_2.setStyleSheet(groupbox_style.replace("groupBox","groupBox_2"))
65
- self.groupBox_3.setStyleSheet(groupbox_style.replace("groupBox","groupBox_3"))
66
-
67
- #scale UI
68
- self.scaleUI()
69
-
70
- except Exception as e:
71
- logger.critical("Error initializing NewEndonuclease class.")
72
- logger.critical(e)
73
- logger.critical(traceback.format_exc())
74
- msgBox = QtWidgets.QMessageBox()
75
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
76
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
77
- msgBox.setWindowTitle("Fatal Error")
78
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
79
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
80
- msgBox.exec()
81
-
82
-
83
- exit(-1)
84
-
85
- #scale UI based on current screen
86
- def scaleUI(self):
87
- try:
88
- self.repaint()
89
- QtWidgets.QApplication.processEvents()
90
-
91
- screen = self.screen()
92
- dpi = screen.physicalDotsPerInch()
93
- width = screen.geometry().width()
94
- height = screen.geometry().height()
95
-
96
- # font scaling
97
- fontSize = 12
98
- self.fontSize = fontSize
99
- self.centralWidget().setStyleSheet("font: " + str(fontSize) + "pt 'Arial';")
100
-
101
- #title scaling
102
- fontSize = 20
103
- self.title.setStyleSheet("font: bold " + str(fontSize) + "pt 'Arial';")
104
-
105
- self.adjustSize()
106
-
107
- currentWidth = self.size().width()
108
- currentHeight = self.size().height()
109
-
110
- # window scaling
111
- scaledWidth = int((width * 480) / 1920)
112
- scaledHeight = int((height * 615) / 1080)
113
-
114
- if scaledHeight < currentHeight:
115
- scaledHeight = currentHeight
116
- if scaledWidth < currentWidth:
117
- scaledWidth = currentWidth
118
-
119
- screen = QtWidgets.QApplication.desktop().screenNumber(QtWidgets.QApplication.desktop().cursor().pos())
120
- centerPoint = QtWidgets.QApplication.desktop().screenGeometry(screen).center()
121
- x = centerPoint.x()
122
- y = centerPoint.y()
123
- x = x - (math.ceil(scaledWidth / 2))
124
- y = y - (math.ceil(scaledHeight / 2))
125
- self.setGeometry(x, y, scaledWidth, scaledHeight)
126
-
127
- self.repaint()
128
- QtWidgets.QApplication.processEvents()
129
-
130
- except Exception as e:
131
- logger.critical("Error in scaleUI() in new endo.")
132
- logger.critical(e)
133
- logger.critical(traceback.format_exc())
134
- msgBox = QtWidgets.QMessageBox()
135
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
136
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
137
- msgBox.setWindowTitle("Fatal Error")
138
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
139
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
140
- msgBox.exec()
141
-
142
-
143
- exit(-1)
144
-
145
- #center UI on current screen
146
- def centerUI(self):
147
- try:
148
- self.repaint()
149
- QtWidgets.QApplication.processEvents()
150
-
151
- #center window on current screen
152
- width = self.width()
153
- height = self.height()
154
- screen = QtWidgets.QApplication.desktop().screenNumber(QtWidgets.QApplication.desktop().cursor().pos())
155
- centerPoint = QtWidgets.QApplication.desktop().screenGeometry(screen).center()
156
- x = centerPoint.x()
157
- y = centerPoint.y()
158
- x = x - (math.ceil(width / 2))
159
- y = y - (math.ceil(height / 2))
160
- self.setGeometry(x, y, width, height)
161
-
162
- self.repaint()
163
- QtWidgets.QApplication.processEvents()
164
- except Exception as e:
165
- logger.critical("Error in centerUI() in new endo.")
166
- logger.critical(e)
167
- logger.critical(traceback.format_exc())
168
- msgBox = QtWidgets.QMessageBox()
169
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
170
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
171
- msgBox.setWindowTitle("Fatal Error")
172
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
173
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
174
- msgBox.exec()
175
-
176
-
177
- exit(-1)
178
-
179
- #helper function for writing new endo information to CASPERinfo - used by submit()
180
- def writeNewEndonuclease(self, newEndonucleaseStr):
181
- try:
182
- with open(GlobalSettings.appdir + 'CASPERinfo', 'r') as f, open(GlobalSettings.appdir + "new_file", 'w+') as f1:
183
- for line in f:
184
- f1.write(line)
185
- if 'ENDONUCLEASES' in line:
186
- f1.write(newEndonucleaseStr + '\n') # Move f1.write(line) above, to write above instead
187
- os.remove(GlobalSettings.appdir + "CASPERinfo")
188
- os.rename(GlobalSettings.appdir + "new_file",
189
- GlobalSettings.appdir + "CASPERinfo") # Rename the new file
190
- except Exception as e:
191
- logger.critical("Error in writeNewEndonuclease() in New Endonuclease.")
192
- logger.critical(e)
193
- logger.critical(traceback.format_exc())
194
- msgBox = QtWidgets.QMessageBox()
195
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
196
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
197
- msgBox.setWindowTitle("Fatal Error")
198
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
199
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
200
- msgBox.exec()
201
-
202
-
203
- exit(-1)
204
-
205
- #submit new endo to CASPERinfo file
206
- def submit(self):
207
- try:
208
- # This is executed when the button is pressed
209
- name = str(self.organism_name.text())
210
- abbr = str(self.abbreviation.text())
211
- crisprtype = str(self.crispr_type.text())
212
- seed_len = str(self.seed_length.text())
213
- five_len = str(self.five_length.text())
214
- three_len = str(self.three_length.text())
215
- pam = str(self.pam_sequence.text()).upper()
216
- ### Check for multiple PAMs and format if present
217
- if len(pam.split(','))>0:
218
- pam = [x.strip() for x in pam.split(',')]
219
- pam = ",".join(pam)
220
- ### Check for PAM directionality
221
- if self.five_pam.isChecked():
222
- pam_dir = str(5)
223
- else:
224
- pam_dir = str(3)
225
- on_scoring = str(self.comboBox.currentText())
226
- off_scoring = str(self.comboBox_2.currentText())
227
- length = len(seed_len) + len(five_len) + len(three_len)
228
- argument_list = [abbr, pam, five_len, seed_len, three_len, pam_dir, name, crisprtype, on_scoring, off_scoring]
229
- validPAM = ('A', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'V', 'W', 'Y')
230
- self.error = False;
231
-
232
- ### Error checking for PAM alphabet
233
- for letter in pam:
234
- if (letter not in validPAM):
235
- msgBox = QtWidgets.QMessageBox()
236
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
237
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
238
- msgBox.setWindowTitle("Invalid PAM")
239
- msgBox.setText("Invalid characters in PAM Sequence.")
240
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
241
- msgBox.exec()
242
- return True
243
- ### Error checking for filling out all fields
244
- for arg in argument_list:
245
- if ';' in arg:
246
- msgBox = QtWidgets.QMessageBox()
247
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
248
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
249
- msgBox.setWindowTitle("Invalid Semicolon")
250
- msgBox.setText("Invalid character used: ';'")
251
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
252
- msgBox.exec()
253
-
254
- return True
255
- elif arg == "":
256
- msgBox = QtWidgets.QMessageBox()
257
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
258
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
259
- msgBox.setWindowTitle("Empty Field")
260
- msgBox.setText("Please fill in all fields.")
261
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
262
- msgBox.exec()
263
-
264
- return True
265
- else:
266
- pass
267
-
268
- ### Check for duplicate endo abbreviations
269
- for key in GlobalSettings.mainWindow.organisms_to_endos:
270
- endo = GlobalSettings.mainWindow.organisms_to_endos[key]
271
- if abbr in endo:
272
- msgBox = QtWidgets.QMessageBox()
273
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
274
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
275
- msgBox.setWindowTitle("Duplicate endo name.")
276
- msgBox.setText("The given abbreviation already exists. Please choose a unique identifier.")
277
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
278
- msgBox.exec()
279
-
280
- return True
281
- else:
282
- pass
283
-
284
- myString = ""
285
- for i, arg in enumerate(argument_list):
286
- if i == len(argument_list)-1: ### Last argument in list
287
- myString += str(arg)
288
- else:
289
- myString += str(arg) + ";"
290
-
291
- self.writeNewEndonuclease(myString)
292
-
293
- ### Refresh endonuclease dropdown in New Genome
294
- GlobalSettings.mainWindow.newGenome.fillEndo()
295
-
296
- self.clear_all()
297
- self.close()
298
- except Exception as e:
299
- logger.critical("Error in submit() in New Endonuclease.")
300
- logger.critical(e)
301
- logger.critical(traceback.format_exc())
302
- msgBox = QtWidgets.QMessageBox()
303
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
304
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
305
- msgBox.setWindowTitle("Fatal Error")
306
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
307
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
308
- msgBox.exec()
309
-
310
-
311
- exit(-1)
312
-
313
- #cancel and close window
314
- def cancel(self):
315
- try:
316
- self.clear_all()
317
- self.close()
318
- except Exception as e:
319
- logger.critical("Error in cancel() in New Endonuclease.")
320
- logger.critical(e)
321
- logger.critical(traceback.format_exc())
322
- msgBox = QtWidgets.QMessageBox()
323
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
324
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
325
- msgBox.setWindowTitle("Fatal Error")
326
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
327
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
328
- msgBox.exec()
329
-
330
-
331
- exit(-1)
332
-
333
- # This function clears all of the line edits
334
- def clear_all(self):
335
- try:
336
- self.organism_name.clear()
337
- self.abbreviation.clear()
338
- self.crispr_type.clear()
339
- self.seed_length.clear()
340
- self.five_length.clear()
341
- self.three_length.clear()
342
- self.pam_sequence.clear()
343
- except Exception as e:
344
- logger.critical("Error in clear_all() in New Endonuclease.")
345
- logger.critical(e)
346
- logger.critical(traceback.format_exc())
347
- msgBox = QtWidgets.QMessageBox()
348
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
349
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
350
- msgBox.setWindowTitle("Fatal Error")
351
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
352
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
353
- msgBox.exec()
354
-
355
-
356
- exit(-1)
357
-
358
- # This function parses CASPERinfo to return the names (in lists) of all on-target and off-target scoring data
359
- def get_on_off_data(self):
360
- try:
361
- filename = GlobalSettings.appdir + "CASPERinfo"
362
- retList_on = []
363
- retList_off = []
364
- with open(filename, 'r') as f:
365
- lines = f.readlines()
366
- for i, line in enumerate(lines):
367
- line = str(line)
368
- if "ON-TARGET DATA" in line:
369
- index = i
370
- while "-----" not in line:
371
- if "DATA:" in line:
372
- retList_on.append(line.split("DATA:")[-1].strip()) ### Append name of scoring data to on-target name list
373
- line = lines[index+1]
374
- index += 1
375
- else:
376
- line = lines[index+1]
377
- index += 1
378
- continue
379
- elif "OFF-TARGET MATRICES" in line:
380
- index = i
381
- while "-----" not in line:
382
- if "MATRIX:" in line:
383
- retList_off.append(line.split("MATRIX:")[-1].strip()) ### Append name of scoring data to off-target name list
384
- line = lines[index+1]
385
- index += 1
386
- else:
387
- line = lines[index+1]
388
- index += 1
389
- continue
390
- else:
391
- continue
392
- return retList_on, retList_off
393
- except Exception as e:
394
- logger.critical("Error in get_on_off_data() in New Endonuclease.")
395
- logger.critical(e)
396
- logger.critical(traceback.format_exc())
397
- msgBox = QtWidgets.QMessageBox()
398
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
399
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
400
- msgBox.setWindowTitle("Fatal Error")
401
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
402
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
403
- msgBox.exec()
404
-
405
-
406
- exit(-1)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
closingWin.py DELETED
@@ -1,197 +0,0 @@
1
- import GlobalSettings
2
- import os
3
- from PyQt5 import QtWidgets, Qt, uic
4
- import traceback
5
- import math
6
-
7
- #global logger
8
- logger = GlobalSettings.logger
9
-
10
- ###########################################################
11
- # closingWindow: this class is a little window where the user can select which files they want to delete
12
- # Once they hit 'submit' it will delete all of the files selected, and close the program.
13
- # If no files are selected, the program closes and no files are deleted
14
- # Inputs are taking from the user (selecting files to delete and hitting submit), as well as GlobalSettings for the files in CSPR_DB
15
- # Outputs are the files are deleting, and the program is closed
16
- ###########################################################
17
- class closingWindow(QtWidgets.QMainWindow):
18
- def __init__(self):
19
- try:
20
- # qt stuff
21
- super(closingWindow, self).__init__()
22
- uic.loadUi(GlobalSettings.appdir + "closing_window.ui", self)
23
- self.setWindowTitle("Delete Files")
24
- self.setWindowIcon(Qt.QIcon(GlobalSettings.appdir + "cas9image.ico"))
25
-
26
- # button connections
27
- self.submit_button.clicked.connect(self.submit_and_close)
28
-
29
- # table stuff
30
- self.files_table.setColumnCount(1)
31
- self.files_table.setShowGrid(True)
32
- self.files_table.setHorizontalHeaderLabels("File Name;".split(";"))
33
- self.files_table.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
34
- self.files_table.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
35
- self.files_table.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
36
-
37
- #scale UI
38
- self.scaleUI()
39
-
40
- except Exception as e:
41
- logger.critical("Error initializing closingWindow class.")
42
- logger.critical(e)
43
- logger.critical(traceback.format_exc())
44
- msgBox = QtWidgets.QMessageBox()
45
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
46
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
47
- msgBox.setWindowTitle("Fatal Error")
48
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
49
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
50
- msgBox.exec()
51
-
52
- exit(-1)
53
-
54
- #scale UI based on current screen
55
- def scaleUI(self):
56
- try:
57
- self.repaint()
58
- QtWidgets.QApplication.processEvents()
59
-
60
- screen = self.screen()
61
- dpi = screen.physicalDotsPerInch()
62
- width = screen.geometry().width()
63
- height = screen.geometry().height()
64
-
65
- # font scaling
66
- fontSize = 12
67
- self.fontSize = fontSize
68
- self.centralWidget().setStyleSheet("font: " + str(fontSize) + "pt 'Arial';")
69
-
70
- self.adjustSize()
71
-
72
- currentWidth = self.size().width()
73
- currentHeight = self.size().height()
74
-
75
- # window scaling
76
- # 1920x1080 => 1150x650
77
- scaledWidth = int((width * 400) / 1920)
78
- scaledHeight = int((height * 300) / 1080)
79
-
80
- if scaledHeight < currentHeight:
81
- scaledHeight = currentHeight
82
- if scaledWidth < currentWidth:
83
- scaledWidth = currentWidth
84
-
85
- screen = QtWidgets.QApplication.desktop().screenNumber(QtWidgets.QApplication.desktop().cursor().pos())
86
- centerPoint = QtWidgets.QApplication.desktop().screenGeometry(screen).center()
87
- x = centerPoint.x()
88
- y = centerPoint.y()
89
- x = x - (math.ceil(scaledWidth / 2))
90
- y = y - (math.ceil(scaledHeight / 2))
91
- self.setGeometry(x, y, scaledWidth, scaledHeight)
92
-
93
- self.repaint()
94
- QtWidgets.QApplication.processEvents()
95
-
96
- except Exception as e:
97
- logger.critical("Error in scaleUI() in closing window.")
98
- logger.critical(e)
99
- logger.critical(traceback.format_exc())
100
- msgBox = QtWidgets.QMessageBox()
101
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
102
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
103
- msgBox.setWindowTitle("Fatal Error")
104
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
105
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
106
- msgBox.exec()
107
-
108
- exit(-1)
109
-
110
- #center UI on current screen
111
- def centerUI(self):
112
- try:
113
- self.repaint()
114
- QtWidgets.QApplication.processEvents()
115
-
116
- #center window on current screen
117
- width = self.width()
118
- height = self.height()
119
- screen = QtWidgets.QApplication.desktop().screenNumber(QtWidgets.QApplication.desktop().cursor().pos())
120
- centerPoint = QtWidgets.QApplication.desktop().screenGeometry(screen).center()
121
- x = centerPoint.x()
122
- y = centerPoint.y()
123
- x = x - (math.ceil(width / 2))
124
- y = y - (math.ceil(height / 2))
125
- self.setGeometry(x, y, width, height)
126
-
127
- self.repaint()
128
- QtWidgets.QApplication.processEvents()
129
- except Exception as e:
130
- logger.critical("Error in centerUI() in closing window.")
131
- logger.critical(e)
132
- logger.critical(traceback.format_exc())
133
- msgBox = QtWidgets.QMessageBox()
134
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
135
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
136
- msgBox.setWindowTitle("Fatal Error")
137
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
138
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
139
- msgBox.exec()
140
-
141
- exit(-1)
142
-
143
- # this function will delete selected files, and then close the program
144
- def submit_and_close(self):
145
- try:
146
- # loop through the whole table
147
- for i in range(self.files_table.rowCount()):
148
- tabWidget = self.files_table.item(i, 0)
149
-
150
- # if that specific tab is selected, delete it. otherwise do nothing
151
- if tabWidget.isSelected():
152
- os.remove(tabWidget.text())
153
-
154
- # close the program now
155
- self.close()
156
- except Exception as e:
157
- logger.critical("Error in sumbit_and_close() in closing window.")
158
- logger.critical(e)
159
- logger.critical(traceback.format_exc())
160
- msgBox = QtWidgets.QMessageBox()
161
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
162
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
163
- msgBox.setWindowTitle("Fatal Error")
164
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
165
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
166
- msgBox.exec()
167
-
168
- exit(-1)
169
-
170
- # this function gets all of the files from the CSPR_DB and puts them all into the table
171
- def get_files(self):
172
- try:
173
- loopCount = 0
174
- # get the file names from CSPR_DB
175
- files_names = os.listdir(GlobalSettings.CSPR_DB)
176
- files_names.sort(key=str.lower)
177
- self.files_table.setRowCount(len(files_names))
178
-
179
- # loop through and add them to the table
180
- for file in files_names:
181
- tabWidget = QtWidgets.QTableWidgetItem(file)
182
- self.files_table.setItem(loopCount, 0, tabWidget)
183
- loopCount += 1
184
- self.files_table.resizeColumnsToContents()
185
- except Exception as e:
186
- logger.critical("Error in get_files() in closing window.")
187
- logger.critical(e)
188
- logger.critical(traceback.format_exc())
189
- msgBox = QtWidgets.QMessageBox()
190
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
191
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
192
- msgBox.setWindowTitle("Fatal Error")
193
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
194
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
195
- msgBox.exec()
196
-
197
- exit(-1)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
common_utils.py DELETED
@@ -1,10 +0,0 @@
1
- from PyQt5 import QtWidgets
2
-
3
- def show_message(fontSize, icon, title, message, button=QtWidgets.QMessageBox.StandardButton.Close):
4
- msgBox = QtWidgets.QMessageBox()
5
- msgBox.setStyleSheet(f"font: {fontSize}pt 'Arial'")
6
- msgBox.setIcon(icon)
7
- msgBox.setWindowTitle(title)
8
- msgBox.setText(message)
9
- msgBox.addButton(button)
10
- msgBox.exec()
 
 
 
 
 
 
 
 
 
 
 
controllers/CoTargeting.py ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from PyQt5 import QtWidgets, Qt, QtGui, QtCore, uic
2
+ import models.GlobalSettings as GlobalSettings
3
+ import traceback
4
+ import math
5
+ from utils.ui import show_message, show_error, scale_ui, center_ui
6
+
7
+ logger = GlobalSettings.logger
8
+
9
+ ######################################################
10
+ # This class is a window that lets the user select which endonucleases to co-target with
11
+ # inputs are from the user and from results
12
+ # from results: the organism name and the list of endonucleases for that organism
13
+ # from user: which endonucleases to co-target
14
+ ######################################################
15
+ class CoTargeting(QtWidgets.QMainWindow):
16
+ def __init__(self, path):
17
+ try:
18
+ super(CoTargeting, self).__init__()
19
+ uic.loadUi(GlobalSettings.appdir + 'ui/cotargeting.ui', self)
20
+ self.setWindowIcon(QtGui.QIcon(GlobalSettings.appdir + "cas9image.ico"))
21
+ self.setWindowTitle("Co-targeting")
22
+
23
+ self.endo_table.setColumnCount(1) # hardcoded because there will always be 1 columns
24
+ self.endo_table.setShowGrid(True)
25
+ self.endo_table.setHorizontalHeaderLabels("Endonuclease;".split(";"))
26
+ self.endo_table.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
27
+ self.endo_table.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
28
+ self.endo_table.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
29
+ self.endo_table.horizontalHeader().setSectionResizeMode(0, QtWidgets.QHeaderView.Stretch) #Ensures last column goes to the edge of table
30
+
31
+ self.info_path = path
32
+
33
+ self.cancel_button.clicked.connect(self.cancel_function)
34
+ self.submit_button.clicked.connect(self.submission_function)
35
+
36
+ scale_ui(self, custom_scale_width=450, custom_scale_height=375)
37
+
38
+ except Exception as e:
39
+ show_error("Error initializing CoTargeting class.", e)
40
+
41
+ # launches the window
42
+ # it is expecting endo_choices in the form of a list, and the orgName in the form of a string
43
+ # it sets the organism name, and sets the table as well
44
+ def launch(self, endo_choices, orgName):
45
+ try:
46
+ self.orgName.setText(orgName)
47
+ setTableList = list()
48
+ # only get the endo choices that were original
49
+ for item in endo_choices:
50
+ checkList = item.split(",")
51
+ if len(checkList) == 1 and "|" not in item: #Prevent cotarget endos from being added back in
52
+ setTableList.append(item)
53
+
54
+ # go through and set each table item, but also set the row count
55
+ self.endo_table.setRowCount(len(setTableList))
56
+ loopCount = 0
57
+ for item in setTableList:
58
+ tabWidget = QtWidgets.QTableWidgetItem(item)
59
+ self.endo_table.setItem(loopCount, 0, tabWidget)
60
+ loopCount += 1
61
+ self.endo_table.resizeColumnsToContents()
62
+
63
+ center_ui(self)
64
+
65
+ self.show()
66
+ self.activateWindow()
67
+ except Exception as e:
68
+ show_error("Error in launch() in CoTargeting.", e)
69
+
70
+ # it clears the table, sets the organism to nothing, and hides the window
71
+ def cancel_function(self):
72
+ try:
73
+ self.endo_table.clearContents()
74
+ self.endo_table.setRowCount(0)
75
+ self.orgName.setText("")
76
+ self.hide()
77
+ except Exception as e:
78
+ show_error("Error in cancel_function() in CoTargeting.", e)
79
+
80
+ # this is the submission function.
81
+ # it makes sure the user selects at least 2 endonucleases
82
+ # then it goes through and returns the endonucleases selected
83
+ # once it gets those, it then calls a function in Results that repopulates the table correctly
84
+ def submission_function(self):
85
+ try:
86
+ #get endo data from CASPERinfo
87
+ self.Endos = {}
88
+ f = open(GlobalSettings.appdir + 'CASPERinfo')
89
+ while True:
90
+ line = f.readline()
91
+ if line.startswith('ENDONUCLEASES'):
92
+ while True:
93
+ line = f.readline()
94
+ if (line[0] == "-"):
95
+ break
96
+ line_tokened = line.split(";")
97
+ endo = line_tokened[0]
98
+ self.Endos[endo] = ([line_tokened[2], line_tokened[3], line_tokened[4]],line_tokened[5])
99
+ break
100
+ f.close()
101
+
102
+ # set the selected_list, and make sure they select at least 2 endonucleases
103
+ selected_list = self.endo_table.selectedItems()
104
+ if len(selected_list) <= 1:
105
+ show_message(
106
+ fontSize=12,
107
+ icon=QtWidgets.QMessageBox.Icon.Critical,
108
+ title="Nothing Selected",
109
+ message="No endonucleases selected. Please select at least 2 endonucleases"
110
+ )
111
+ return
112
+
113
+ # go through and get which endonuclease's have been selected
114
+ ret_endo_list = list()
115
+ for i in range(self.endo_table.rowCount()):
116
+ if self.endo_table.item(i, 0).isSelected():
117
+ ret_endo_list.append(self.endo_table.item(i, 0).text())
118
+
119
+ #invalid_flag = False
120
+ for endo1 in ret_endo_list:
121
+ for endo2 in ret_endo_list:
122
+ if endo1 == endo2:
123
+ continue
124
+ endo1_len = sum([int(x) for x in self.Endos[endo1][0]])
125
+ endo2_len = sum([int(x) for x in self.Endos[endo2][0]])
126
+ if endo1_len != endo2_len or self.Endos[endo1][1] != self.Endos[endo2][1]: # If endonucleases don't have the same length gRNA or don't have the same directionality, throw an error
127
+ show_message(
128
+ fontSize=12,
129
+ icon=QtWidgets.QMessageBox.Icon.Critical,
130
+ title="Invalid Endonucleases",
131
+ message="The selected endonucleases are not compatible."
132
+ )
133
+ return
134
+
135
+ GlobalSettings.mainWindow.Results.co_target_endo_list = ret_endo_list
136
+ GlobalSettings.mainWindow.Results.populate_cotarget_table()
137
+ self.cancel_function()
138
+ except Exception as e:
139
+ show_error("Error in submission_function() in CoTargeting.", e)
OffTarget.py → controllers/OffTarget.py RENAMED
@@ -1,22 +1,16 @@
1
  import os, platform
2
  from PyQt5 import QtWidgets, uic, QtCore, QtGui, Qt
3
  from functools import partial
4
- import GlobalSettings
5
- import gzip
6
- import traceback
7
- import math
8
- from error_handling import show_error
9
- from common_utils import show_message
10
 
11
- #global logger
12
  logger = GlobalSettings.logger
13
 
14
  class OffTarget(QtWidgets.QMainWindow):
15
-
16
  def __init__(self):
17
  try:
18
  super(OffTarget, self).__init__()
19
- uic.loadUi(GlobalSettings.appdir + 'off_target.ui', self)
20
  self.setWindowIcon(Qt.QIcon(GlobalSettings.appdir + "cas9image.ico"))
21
  self.setWindowTitle("Off-Target Analysis")
22
  self.progressBar.setMinimum(0)
@@ -35,8 +29,6 @@ class OffTarget(QtWidgets.QMainWindow):
35
  self.bool_temp = False
36
  self.running = False
37
  self.process = QtCore.QProcess()
38
-
39
- # make sure to intialize the class variable in init. That way elsewhere and other classes can access it
40
  self.output_path = ''
41
 
42
  groupbox_style = """
@@ -52,117 +44,10 @@ class OffTarget(QtWidgets.QMainWindow):
52
  self.Step2.setStyleSheet(groupbox_style.replace("Step1", "Step2"))
53
  self.Step3.setStyleSheet(groupbox_style.replace("Step1", "Step3"))
54
 
55
- #scale UI
56
- self.scaleUI()
57
-
58
- except Exception as e:
59
- logger.critical("Error initializing OffTarget class.")
60
- logger.critical(e)
61
- logger.critical(traceback.format_exc())
62
- msgBox = QtWidgets.QMessageBox()
63
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
64
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
65
- msgBox.setWindowTitle("Fatal Error")
66
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
67
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
68
- msgBox.exec()
69
-
70
- exit(-1)
71
-
72
- #scale UI based on current screen
73
- def scaleUI(self):
74
- try:
75
- self.repaint()
76
- QtWidgets.QApplication.processEvents()
77
-
78
- screen = self.screen()
79
- dpi = screen.physicalDotsPerInch()
80
- width = screen.geometry().width()
81
- height = screen.geometry().height()
82
-
83
- # font scaling
84
- fontSize = 12
85
- self.fontSize = fontSize
86
- self.centralWidget().setStyleSheet("font: " + str(fontSize) + "pt 'Arial';")
87
-
88
- # CASPER header scaling
89
- fontSize = 20
90
- self.title.setStyleSheet("font: bold " + str(fontSize) + "pt 'Arial';")
91
-
92
- self.adjustSize()
93
-
94
- currentWidth = self.size().width()
95
- currentHeight = self.size().height()
96
 
97
- # window scaling
98
- # 1920x1080 => 850x750
99
- scaledWidth = int((width * 400) / 1920)
100
- scaledHeight = int((height * 450) / 1080)
101
-
102
- if scaledHeight < currentHeight:
103
- scaledHeight = currentHeight
104
- if scaledWidth < currentWidth:
105
- scaledWidth = currentWidth
106
-
107
- screen = QtWidgets.QApplication.desktop().screenNumber(QtWidgets.QApplication.desktop().cursor().pos())
108
- centerPoint = QtWidgets.QApplication.desktop().screenGeometry(screen).center()
109
- x = centerPoint.x()
110
- y = centerPoint.y()
111
- x = x - (math.ceil(scaledWidth / 2))
112
- y = y - (math.ceil(scaledHeight / 2))
113
- self.setGeometry(x, y, scaledWidth, scaledHeight)
114
-
115
- self.repaint()
116
- QtWidgets.QApplication.processEvents()
117
-
118
- except Exception as e:
119
- logger.critical("Error in scaleUI() in Off-Target.")
120
- logger.critical(e)
121
- logger.critical(traceback.format_exc())
122
- msgBox = QtWidgets.QMessageBox()
123
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
124
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
125
- msgBox.setWindowTitle("Fatal Error")
126
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
127
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
128
- msgBox.exec()
129
-
130
-
131
- exit(-1)
132
-
133
- #center UI on current screen
134
- def centerUI(self):
135
- try:
136
- self.repaint()
137
- QtWidgets.QApplication.processEvents()
138
-
139
- # center window on current screen
140
- width = self.width()
141
- height = self.height()
142
- screen = QtWidgets.QApplication.desktop().screenNumber(QtWidgets.QApplication.desktop().cursor().pos())
143
- centerPoint = QtWidgets.QApplication.desktop().screenGeometry(screen).center()
144
- x = centerPoint.x()
145
- y = centerPoint.y()
146
- x = x - (math.ceil(width / 2))
147
- y = y - (math.ceil(height / 2))
148
- self.setGeometry(x, y, width, height)
149
-
150
- self.repaint()
151
- QtWidgets.QApplication.processEvents()
152
  except Exception as e:
153
- logger.critical("Error in centerUI() in Off-Target.")
154
- logger.critical(e)
155
- logger.critical(traceback.format_exc())
156
- msgBox = QtWidgets.QMessageBox()
157
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
158
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
159
- msgBox.setWindowTitle("Fatal Error")
160
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
161
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
162
- msgBox.exec()
163
-
164
-
165
- exit(-1)
166
 
167
  #copied from MT to fill in the chromo and endo dropdowns based on CSPR files user provided at the startup
168
  def fill_data_dropdown(self):
@@ -226,19 +111,7 @@ class OffTarget(QtWidgets.QMainWindow):
226
  self.mismatchcomboBox.addItems(mismatch_list)
227
  self.mismatchcomboBox.setCurrentIndex(3) ### Max number of mismatches is 4 by default
228
  except Exception as e:
229
- logger.critical("Error in fill_data_dropdown() in OffTarget.")
230
- logger.critical(e)
231
- logger.critical(traceback.format_exc())
232
- msgBox = QtWidgets.QMessageBox()
233
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
234
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
235
- msgBox.setWindowTitle("Fatal Error")
236
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
237
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
238
- msgBox.exec()
239
-
240
-
241
- exit(-1)
242
 
243
  def change_endos(self):
244
  try:
@@ -246,20 +119,8 @@ class OffTarget(QtWidgets.QMainWindow):
246
  self.cspr_file = self.organisms_to_files[str(self.OrgcomboBox.currentText())][str(self.EndocomboBox.currentText())][0]
247
  self.db_file = self.organisms_to_files[str(self.OrgcomboBox.currentText())][str(self.EndocomboBox.currentText())][1]
248
  except Exception as e:
249
- logger.critical("Error in change_endos() in OffTarget.")
250
- logger.critical(e)
251
- logger.critical(traceback.format_exc())
252
- msgBox = QtWidgets.QMessageBox()
253
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
254
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
255
- msgBox.setWindowTitle("Fatal Error")
256
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
257
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
258
- msgBox.exec()
259
-
260
-
261
- exit(-1)
262
-
263
  def update_endos(self):
264
  try:
265
  #try to disconnect index changed signal on endo dropdown if there is one
@@ -278,20 +139,8 @@ class OffTarget(QtWidgets.QMainWindow):
278
  #reconnect index changed signal on endo dropdown
279
  self.EndocomboBox.currentIndexChanged.connect(self.change_endos)
280
  except Exception as e:
281
- logger.critical("Error in update_endos() in OffTarget.")
282
- logger.critical(e)
283
- logger.critical(traceback.format_exc())
284
- msgBox = QtWidgets.QMessageBox()
285
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
286
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
287
- msgBox.setWindowTitle("Fatal Error")
288
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
289
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
290
- msgBox.exec()
291
-
292
-
293
- exit(-1)
294
-
295
  #tolerance slider / entry box. Allows for slider to update, or the user to input in text box
296
  # def tol_change(self):
297
  # try:
@@ -324,7 +173,7 @@ class OffTarget(QtWidgets.QMainWindow):
324
  return
325
 
326
  if save_internally:
327
- full_output_path = os.path.join(GlobalSettings.appdir, 'local_output.txt')
328
  else:
329
  full_output_path = os.path.join(GlobalSettings.CSPR_DB, output_file_name)
330
  if os.path.isfile(full_output_path):
@@ -341,42 +190,21 @@ class OffTarget(QtWidgets.QMainWindow):
341
  self.bool_temp = False
342
  self.running = False
343
 
344
- # #setup arguments for C++ .exe
345
  app_path = GlobalSettings.appdir.replace('\\','/')
346
  exe_path = os.path.join(app_path, 'OffTargetFolder', 'OT_Win.exe' if platform.system() == 'Windows' else 'OT_Lin' if platform.system() == 'Linux' else 'OT_Mac')
347
- # data_path = os.path.join(app_path, 'OffTargetFolder', 'temp.txt')
348
- # db_path = os.path.join(GlobalSettings.CSPR_DB, self.db_file)
349
- # CASPER_info_path = os.path.join(app_path, 'CASPERinfo')
350
- # cspr_path = os.path.join(GlobalSettings.CSPR_DB, self.cspr_file)
 
351
  num_of_mismatches = int(self.mismatchcomboBox.currentText())
352
- # endo = self.EndocomboBox.currentText()
353
- # hsu = GlobalSettings.mainWindow.Results.endo_data[self.EndocomboBox.currentText()][2]
354
-
355
- # cmd = f'"{exe_path}" "{data_path}" "{endo}" "{cspr_path}" "{db_path}" "{full_output_path}" "{CASPER_info_path}" {num_of_mismatches} {self.tolerance} {"TRUE" if self.AVG.isChecked() else "FALSE"} {"FALSE" if self.AVG.isChecked() else "TRUE"} "{hsu}"'
356
-
357
-
358
- if (self.AVG.isChecked()):
359
- avg_output = r'TRUE'
360
- detailed_output = r' FALSE '
361
- else:
362
- avg_output = r'FALSE'
363
- detailed_output = r' TRUE '
364
 
365
- data_path = ' "' + app_path + 'OffTargetFolder/temp.txt' + '"' ##
366
- cspr_path = ' "' + GlobalSettings.CSPR_DB + '/' + self.cspr_file + '"'
367
- db_path = ' "' + GlobalSettings.CSPR_DB + '/' + self.db_file + '"'
368
  self.output_path = ' "' + full_output_path + '"'
369
- CASPER_info_path = r' "' + app_path + 'CASPERinfo' + '" '
370
- endo = ' "' + self.EndocomboBox.currentText() + '"'
371
- hsu = ' "' + GlobalSettings.mainWindow.Results.endo_data[self.EndocomboBox.currentText()][2] + '"'
372
-
373
- #create command string
374
- # cmd = f'"{exe_path}" "{data_path}" "{endo}" "{cspr_path}" "{db_path}" "{full_output_path}" "{CASPER_info_path}" {num_of_mismatches} {self.tolerance} {"TRUE" if self.AVG.isChecked() else "FALSE"} {"FALSE" if self.AVG.isChecked() else "TRUE"} "{hsu}"'
375
- cmd = f'"{exe_path}"' + data_path + endo + cspr_path + db_path + self.output_path + CASPER_info_path + str(num_of_mismatches) + ' ' + str(self.tolerance) + detailed_output + avg_output + hsu
376
  cmd = cmd.replace('/', '\\') if platform.system() == 'Windows' else cmd
377
 
378
-
379
- #used to know when the process is done
380
  def finished():
381
  self.running = False
382
  self.run_clicked = True
@@ -415,20 +243,8 @@ class OffTarget(QtWidgets.QMainWindow):
415
  self.running = True
416
  self.run_command()
417
  except Exception as e:
418
- logger.critical("Error in run_analysis() in OffTarget.")
419
- logger.critical(e)
420
- logger.critical(traceback.format_exc())
421
- msgBox = QtWidgets.QMessageBox()
422
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
423
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
424
- msgBox.setWindowTitle("Fatal Error")
425
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
426
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
427
- msgBox.exec()
428
-
429
-
430
- exit(-1)
431
-
432
  #exit linked to user clicking cancel, resets bools, and kills process if one was running
433
  def exit(self):
434
  try:
@@ -438,7 +254,7 @@ class OffTarget(QtWidgets.QMainWindow):
438
  self.process.kill()
439
  self.hide()
440
 
441
- local_output_path = os.path.join(GlobalSettings.appdir, 'local_output.txt')
442
 
443
  if os.path.exists(local_output_path):
444
  os.remove(local_output_path)
@@ -446,20 +262,8 @@ class OffTarget(QtWidgets.QMainWindow):
446
  else:
447
  print("Local output file does not exist.")
448
  except Exception as e:
449
- logger.critical("Error in exit() in OffTarget.")
450
- logger.critical(e)
451
- logger.critical(traceback.format_exc())
452
- msgBox = QtWidgets.QMessageBox()
453
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
454
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
455
- msgBox.setWindowTitle("Fatal Error")
456
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
457
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
458
- msgBox.exec()
459
-
460
-
461
- exit(-1)
462
-
463
  #closeEvent linked to user pressing the x in the top right of windows, resets bools, and
464
  #kills process if there was one running
465
  def closeEvent(self, event):
@@ -470,17 +274,4 @@ class OffTarget(QtWidgets.QMainWindow):
470
  self.running = False
471
  event.accept()
472
  except Exception as e:
473
- logger.critical("Error in closeEvent() in OffTarget.")
474
- logger.critical(e)
475
- logger.critical(traceback.format_exc())
476
- msgBox = QtWidgets.QMessageBox()
477
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
478
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
479
- msgBox.setWindowTitle("Fatal Error")
480
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
481
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
482
- msgBox.exec()
483
-
484
-
485
- exit(-1)
486
-
 
1
  import os, platform
2
  from PyQt5 import QtWidgets, uic, QtCore, QtGui, Qt
3
  from functools import partial
4
+ import models.GlobalSettings as GlobalSettings
5
+ from utils.ui import show_message, show_error, scale_ui, center_ui
 
 
 
 
6
 
 
7
  logger = GlobalSettings.logger
8
 
9
  class OffTarget(QtWidgets.QMainWindow):
 
10
  def __init__(self):
11
  try:
12
  super(OffTarget, self).__init__()
13
+ uic.loadUi(GlobalSettings.appdir + 'ui/off_target.ui', self)
14
  self.setWindowIcon(Qt.QIcon(GlobalSettings.appdir + "cas9image.ico"))
15
  self.setWindowTitle("Off-Target Analysis")
16
  self.progressBar.setMinimum(0)
 
29
  self.bool_temp = False
30
  self.running = False
31
  self.process = QtCore.QProcess()
 
 
32
  self.output_path = ''
33
 
34
  groupbox_style = """
 
44
  self.Step2.setStyleSheet(groupbox_style.replace("Step1", "Step2"))
45
  self.Step3.setStyleSheet(groupbox_style.replace("Step1", "Step3"))
46
 
47
+ scale_ui(self, custom_scale_width=400, custom_scale_height=450)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  except Exception as e:
50
+ show_error("Error initializing OffTarget class.", e)
 
 
 
 
 
 
 
 
 
 
 
 
51
 
52
  #copied from MT to fill in the chromo and endo dropdowns based on CSPR files user provided at the startup
53
  def fill_data_dropdown(self):
 
111
  self.mismatchcomboBox.addItems(mismatch_list)
112
  self.mismatchcomboBox.setCurrentIndex(3) ### Max number of mismatches is 4 by default
113
  except Exception as e:
114
+ show_error("Error in fill_data_dropdown() in OffTarget.", e)
 
 
 
 
 
 
 
 
 
 
 
 
115
 
116
  def change_endos(self):
117
  try:
 
119
  self.cspr_file = self.organisms_to_files[str(self.OrgcomboBox.currentText())][str(self.EndocomboBox.currentText())][0]
120
  self.db_file = self.organisms_to_files[str(self.OrgcomboBox.currentText())][str(self.EndocomboBox.currentText())][1]
121
  except Exception as e:
122
+ show_error("Error in change_endos() in OffTarget.", e)
123
+
 
 
 
 
 
 
 
 
 
 
 
 
124
  def update_endos(self):
125
  try:
126
  #try to disconnect index changed signal on endo dropdown if there is one
 
139
  #reconnect index changed signal on endo dropdown
140
  self.EndocomboBox.currentIndexChanged.connect(self.change_endos)
141
  except Exception as e:
142
+ show_error("Error in update_endos() in OffTarget.", e)
143
+
 
 
 
 
 
 
 
 
 
 
 
 
144
  #tolerance slider / entry box. Allows for slider to update, or the user to input in text box
145
  # def tol_change(self):
146
  # try:
 
173
  return
174
 
175
  if save_internally:
176
+ full_output_path = os.path.join(GlobalSettings.appdir, 'local/local_output.txt')
177
  else:
178
  full_output_path = os.path.join(GlobalSettings.CSPR_DB, output_file_name)
179
  if os.path.isfile(full_output_path):
 
190
  self.bool_temp = False
191
  self.running = False
192
 
 
193
  app_path = GlobalSettings.appdir.replace('\\','/')
194
  exe_path = os.path.join(app_path, 'OffTargetFolder', 'OT_Win.exe' if platform.system() == 'Windows' else 'OT_Lin' if platform.system() == 'Linux' else 'OT_Mac')
195
+ data_path = os.path.join(app_path, 'OffTargetFolder', 'temp.txt')
196
+ endo = self.EndocomboBox.currentText()
197
+ cspr_path = os.path.join(GlobalSettings.CSPR_DB, self.cspr_file)
198
+ db_path = os.path.join(GlobalSettings.CSPR_DB, self.db_file)
199
+ CASPER_info_path = os.path.join(app_path, 'CASPERinfo')
200
  num_of_mismatches = int(self.mismatchcomboBox.currentText())
201
+ hsu = GlobalSettings.mainWindow.Results.endo_data[self.EndocomboBox.currentText()][2]
 
 
 
 
 
 
 
 
 
 
 
202
 
 
 
 
203
  self.output_path = ' "' + full_output_path + '"'
204
+
205
+ cmd = f'"{exe_path}" "{data_path}" "{endo}" "{cspr_path}" "{db_path}" "{full_output_path}" "{CASPER_info_path}" {num_of_mismatches} {self.tolerance} {"TRUE" if self.AVG.isChecked() else "FALSE"} {"FALSE" if self.AVG.isChecked() else "TRUE"} "{hsu}"'
 
 
 
 
 
206
  cmd = cmd.replace('/', '\\') if platform.system() == 'Windows' else cmd
207
 
 
 
208
  def finished():
209
  self.running = False
210
  self.run_clicked = True
 
243
  self.running = True
244
  self.run_command()
245
  except Exception as e:
246
+ show_error("Error in run_analysis() in OffTarget.", e)
247
+
 
 
 
 
 
 
 
 
 
 
 
 
248
  #exit linked to user clicking cancel, resets bools, and kills process if one was running
249
  def exit(self):
250
  try:
 
254
  self.process.kill()
255
  self.hide()
256
 
257
+ local_output_path = os.path.join(GlobalSettings.appdir, 'local/local_output.txt')
258
 
259
  if os.path.exists(local_output_path):
260
  os.remove(local_output_path)
 
262
  else:
263
  print("Local output file does not exist.")
264
  except Exception as e:
265
+ show_error("Error in exit() in OffTarget.", e)
266
+
 
 
 
 
 
 
 
 
 
 
 
 
267
  #closeEvent linked to user pressing the x in the top right of windows, resets bools, and
268
  #kills process if there was one running
269
  def closeEvent(self, event):
 
274
  self.running = False
275
  event.accept()
276
  except Exception as e:
277
+ show_error("Error in closeEvent() in OffTarget.", e)
 
 
 
 
 
 
 
 
 
 
 
 
 
Results.py → controllers/Results.py RENAMED
@@ -1,14 +1,15 @@
1
- from Algorithms import get_table_headers
2
  from PyQt5 import QtWidgets, uic, QtCore, QtGui, Qt
3
  from Bio.Seq import Seq
4
  from Bio import SeqIO
5
- from CSPRparser import CSPRparser
6
- import GlobalSettings
7
- import OffTarget
8
  import platform
9
  import traceback
10
  import math
11
- from scoring_window import Scoring_Window
 
12
 
13
 
14
  #global logger
@@ -24,7 +25,7 @@ class Results(QtWidgets.QMainWindow):
24
  def __init__(self, parent=None):
25
  try:
26
  super(Results, self).__init__(parent)
27
- uic.loadUi(GlobalSettings.appdir + 'results.ui', self)
28
  self.setWindowIcon(Qt.QIcon(GlobalSettings.appdir + "cas9image.ico"))
29
  self.setWindowTitle('Results')
30
  self.geneViewer.setReadOnly(True)
@@ -1271,7 +1272,7 @@ class Results(QtWidgets.QMainWindow):
1271
  self.first_boot = False
1272
  self.off_tar_win = OffTarget.OffTarget()
1273
  self.off_tar_win.submitButton.clicked.connect(self.refresh_data)
1274
- self.off_tar_win.centerUI()
1275
  ref_org = str(GlobalSettings.mainWindow.orgChoice.currentText()) ### Set default reference organism to the organism that is being targeted
1276
  index = self.off_tar_win.OrgcomboBox.findText(ref_org) ### Find organism in combo box list
1277
  self.off_tar_win.OrgcomboBox.setCurrentIndex(index) ### Set combo box to appropriate index
@@ -1644,7 +1645,7 @@ class Filter_Options(QtWidgets.QMainWindow):
1644
  def __init__(self, parent=None):
1645
  try:
1646
  super(Filter_Options, self).__init__(parent)
1647
- uic.loadUi(GlobalSettings.appdir + 'filter_options.ui', self)
1648
  self.setWindowIcon(Qt.QIcon(GlobalSettings.appdir + "cas9image.ico"))
1649
  self.setWindowTitle("Filter Options")
1650
  self.minScoreLine.setText("0")
 
1
+ from utils.Algorithms import get_table_headers
2
  from PyQt5 import QtWidgets, uic, QtCore, QtGui, Qt
3
  from Bio.Seq import Seq
4
  from Bio import SeqIO
5
+ from models.CSPRparser import CSPRparser
6
+ import models.GlobalSettings as GlobalSettings
7
+ import controllers.OffTarget as OffTarget
8
  import platform
9
  import traceback
10
  import math
11
+ from controllers.scoring_window import Scoring_Window
12
+ from utils.ui import scale_ui, center_ui
13
 
14
 
15
  #global logger
 
25
  def __init__(self, parent=None):
26
  try:
27
  super(Results, self).__init__(parent)
28
+ uic.loadUi(GlobalSettings.appdir + 'ui/results.ui', self)
29
  self.setWindowIcon(Qt.QIcon(GlobalSettings.appdir + "cas9image.ico"))
30
  self.setWindowTitle('Results')
31
  self.geneViewer.setReadOnly(True)
 
1272
  self.first_boot = False
1273
  self.off_tar_win = OffTarget.OffTarget()
1274
  self.off_tar_win.submitButton.clicked.connect(self.refresh_data)
1275
+ center_ui(self.off_tar_win)
1276
  ref_org = str(GlobalSettings.mainWindow.orgChoice.currentText()) ### Set default reference organism to the organism that is being targeted
1277
  index = self.off_tar_win.OrgcomboBox.findText(ref_org) ### Find organism in combo box list
1278
  self.off_tar_win.OrgcomboBox.setCurrentIndex(index) ### Set combo box to appropriate index
 
1645
  def __init__(self, parent=None):
1646
  try:
1647
  super(Filter_Options, self).__init__(parent)
1648
+ uic.loadUi(GlobalSettings.appdir + 'ui/filter_options.ui', self)
1649
  self.setWindowIcon(Qt.QIcon(GlobalSettings.appdir + "cas9image.ico"))
1650
  self.setWindowTitle("Filter Options")
1651
  self.minScoreLine.setText("0")
multitargeting.py → controllers/multitargeting.py RENAMED
@@ -1,10 +1,10 @@
1
  from PyQt5 import QtWidgets, Qt, QtGui, QtCore, uic
2
  from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
3
  from matplotlib.figure import Figure
4
- import GlobalSettings
5
  import matplotlib
6
- from Algorithms import SeqTranslate
7
- from CSPRparser import CSPRparser
8
  from matplotlib.ticker import MaxNLocator
9
  import os
10
  import sqlite3
@@ -18,8 +18,8 @@ from matplotlib.widgets import Slider
18
  from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
19
  from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
20
  import matplotlib.pyplot as plt
21
-
22
- #global logger
23
  logger = GlobalSettings.logger
24
 
25
  class Multitargeting(QtWidgets.QMainWindow):
@@ -27,7 +27,7 @@ class Multitargeting(QtWidgets.QMainWindow):
27
  try:
28
  self.count = 0
29
  super(Multitargeting, self).__init__()
30
- uic.loadUi(GlobalSettings.appdir + 'mt.ui', self)
31
  self.setWindowIcon(Qt.QIcon(GlobalSettings.appdir + "cas9image.ico"))
32
  self.multitargeting_statistics = Multitargeting_Statistics()
33
 
@@ -85,7 +85,7 @@ class Multitargeting(QtWidgets.QMainWindow):
85
  self.selectAll.stateChanged.connect(self.select_all)
86
  self.selectAll.setEnabled(False)
87
 
88
- # go back to main button
89
  self.back_button.clicked.connect(self.go_back)
90
 
91
  # Statistics storage variables
@@ -98,14 +98,12 @@ class Multitargeting(QtWidgets.QMainWindow):
98
  self.bar_coords = []
99
  self.seed_id_seq_pair = {}
100
 
101
- # parser object
102
  self.parser = CSPRparser("")
103
 
104
  self.ready_chromo_min_max = True
105
  self.ready_chromo_make_graph = True
106
  self.info_path = os.getcwd()
107
 
108
- ##################################
109
  self.scene = QtWidgets.QGraphicsScene()
110
  self.scene2 = QtWidgets.QGraphicsScene()
111
  self.graphicsView_2.setScene(self.scene2)
@@ -120,22 +118,14 @@ class Multitargeting(QtWidgets.QMainWindow):
120
  self.sql_settings = sql_query_settings()
121
  self.sql_settings.row_count.textChanged.connect(self.sql_row_count_value_changed)
122
 
123
- #scale UI
124
  self.first_show = True
125
- self.scaleUI()
 
 
 
126
 
127
  except Exception as e:
128
- logger.critical("Error initializing Multi-targeting.")
129
- logger.critical(e)
130
- logger.critical(traceback.format_exc())
131
- msgBox = QtWidgets.QMessageBox()
132
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
133
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
134
- msgBox.setWindowTitle("Fatal Error")
135
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
136
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
137
- msgBox.exec()
138
- exit(-1)
139
 
140
  def select_all(self):
141
  try:
@@ -144,164 +134,39 @@ class Multitargeting(QtWidgets.QMainWindow):
144
  else:
145
  self.table.clearSelection()
146
  except Exception as e:
147
- logger.critical("Error in selectAll() in multitargeting.")
148
- logger.critical(e)
149
- logger.critical(traceback.format_exc())
150
- msgBox = QtWidgets.QMessageBox()
151
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
152
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
153
- msgBox.setWindowTitle("Fatal Error")
154
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
155
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
156
- msgBox.exec()
157
-
158
-
159
- exit(-1)
160
-
161
-
162
- def scaleUI(self):
163
- try:
164
- self.repaint()
165
- QtWidgets.QApplication.processEvents()
166
-
167
- screen = self.screen()
168
- dpi = screen.physicalDotsPerInch()
169
- self.dpi = dpi
170
- width = screen.geometry().width()
171
- height = screen.geometry().height()
172
-
173
- # font scaling
174
- fontSize = 12
175
- self.fontSize = fontSize
176
- self.centralWidget().setStyleSheet("font: " + str(fontSize) + "pt 'Arial';" )
177
- self.menuBar().setStyleSheet("font: " + str(fontSize) + "pt 'Arial';" )
178
-
179
- #CASPER header scaling
180
- fontSize = 30
181
- self.title.setStyleSheet("font: bold " + str(fontSize) + "pt 'Arial';")
182
-
183
- self.adjustSize()
184
-
185
- currentWidth = self.size().width()
186
- currentHeight = self.size().height()
187
-
188
- #make sure chromosome viewer doesnt get too small
189
- self.groupBox_2.setMinimumHeight(int(0.3 * height))
190
-
191
- # window scaling
192
- scaledWidth = int((width * 1400) / 1920)
193
- scaledHeight = int((height * 900) / 1080)
194
-
195
- if scaledHeight < currentHeight:
196
- scaledHeight = currentHeight
197
- if scaledWidth < currentWidth:
198
- scaledWidth = currentWidth
199
-
200
- #set min width of table
201
- self.table.setMinimumWidth(int(0.5 * scaledWidth))
202
-
203
- screen = QtWidgets.QApplication.desktop().screenNumber(QtWidgets.QApplication.desktop().cursor().pos())
204
- centerPoint = QtWidgets.QApplication.desktop().screenGeometry(screen).center()
205
- x = centerPoint.x()
206
- y = centerPoint.y()
207
- x = x - (math.ceil(scaledWidth / 2))
208
- y = y - (math.ceil(scaledHeight / 2))
209
-
210
- self.setGeometry(x, y, scaledWidth, scaledHeight)
211
- self.repaint()
212
- QtWidgets.QApplication.processEvents()
213
-
214
- except Exception as e:
215
- logger.critical("Error in scaleUI() in multi-targeting.")
216
- logger.critical(e)
217
- logger.critical(traceback.format_exc())
218
- msgBox = QtWidgets.QMessageBox()
219
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
220
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
221
- msgBox.setWindowTitle("Fatal Error")
222
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
223
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
224
- msgBox.exec()
225
-
226
- exit(-1)
227
-
228
- def centerUI(self):
229
- self.repaint()
230
- QtWidgets.QApplication.processEvents()
231
-
232
- width = self.width()
233
- height = self.height()
234
- # scale/center window
235
- screen = QtWidgets.QApplication.desktop().screenNumber(QtWidgets.QApplication.desktop().cursor().pos())
236
- centerPoint = QtWidgets.QApplication.desktop().screenGeometry(screen).center()
237
- x = centerPoint.x()
238
- y = centerPoint.y()
239
- x = x - (math.ceil(width / 2))
240
- y = y - (math.ceil(height / 2))
241
- self.setGeometry(x, y, width, height)
242
-
243
- self.Analyze_Button.resize(200, 200)
244
-
245
- self.repaint()
246
- QtWidgets.QApplication.processEvents()
247
 
248
  def export_tool(self):
249
  try:
250
  select_items = self.table.selectedItems()
251
  if len(select_items) <= 0:
252
- msgBox = QtWidgets.QMessageBox()
253
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
254
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
255
- msgBox.setWindowTitle("Nothing Selected")
256
- msgBox.setText("No targets were highlighted. Please highlight the targets you want to be exported to a CSV File!")
257
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
258
- msgBox.exec()
259
-
260
  return
261
  GlobalSettings.mainWindow.export_tool_window.launch(select_items,"mt")
262
  except Exception as e:
263
- logger.critical("Error in export_tool() in multi-targeting.")
264
- logger.critical(e)
265
- logger.critical(traceback.format_exc())
266
- msgBox = QtWidgets.QMessageBox()
267
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
268
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
269
- msgBox.setWindowTitle("Fatal Error")
270
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
271
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
272
- msgBox.exec()
273
-
274
- exit(-1)
275
 
276
  def show_statistics(self):
277
  try:
278
  if (self.line_bool and self.bar_bool):
279
- self.multitargeting_statistics.centerUI()
280
  self.multitargeting_statistics.show()
281
  self.multitargeting_statistics.activateWindow()
282
  else:
283
- msgBox = QtWidgets.QMessageBox()
284
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
285
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
286
- msgBox.setWindowTitle("No analysis run.")
287
- msgBox.setText('Multitargeting Analysis must be performed before viewing statistics.\n\nSelect an organism and endonuclease and click "Analyze" then try again.')
288
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
289
- msgBox.exec()
290
-
291
  return True
292
  except Exception as e:
293
- logger.critical("Error in show_statistics() in multi-targeting.")
294
- logger.critical(e)
295
- logger.critical(traceback.format_exc())
296
- msgBox = QtWidgets.QMessageBox()
297
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
298
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
299
- msgBox.setWindowTitle("Fatal Error")
300
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
301
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
302
- msgBox.exec()
303
-
304
- exit(-1)
305
 
306
  #event handler to show details of targets in chromosome viewer while hovering over canvases
307
  def chromosome_event_handler(self, event):
@@ -338,64 +203,29 @@ class Multitargeting(QtWidgets.QMainWindow):
338
  text.setFont(font)
339
 
340
  except Exception as e:
341
- logger.critical("Error in event_data() in multi-targeting.")
342
- logger.critical(e)
343
- logger.critical(traceback.format_exc())
344
- msgBox = QtWidgets.QMessageBox()
345
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
346
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
347
- msgBox.setWindowTitle("Fatal Error")
348
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
349
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
350
- msgBox.exec()
351
-
352
- exit(-1)
353
 
354
  def launch(self):
355
  try:
356
  self.get_data()
357
  except Exception as e:
358
- logger.critical("Error in launch() in multi-targeting.")
359
- logger.critical(e)
360
- logger.critical(traceback.format_exc())
361
- msgBox = QtWidgets.QMessageBox()
362
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
363
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
364
- msgBox.setWindowTitle("Fatal Error")
365
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
366
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
367
- msgBox.exec()
368
-
369
- exit(-1)
370
-
371
  #button trigger for sql settings
372
  def update_sql_query_settings(self):
373
  try:
374
- self.sql_settings.centerUI()
375
  self.sql_settings.show()
376
  self.sql_settings.activateWindow()
377
  except Exception as e:
378
- logger.critical("Error in update_sql_query_settings() in multi-targeting.")
379
- logger.critical(e)
380
- logger.critical(traceback.format_exc())
381
- msgBox = QtWidgets.QMessageBox()
382
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
383
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
384
- msgBox.setWindowTitle("Fatal Error")
385
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
386
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
387
- msgBox.exec()
388
-
389
- exit(-1)
390
-
391
  #trigger for if sql line edit value has changed
392
  def sql_row_count_value_changed(self):
393
  try:
394
  self.row_limit = int(self.sql_settings.row_count.text())
395
  except Exception as e:
396
- logger.critical("Error in sql_row_count_value_changed() in multi-targeting.")
397
- logger.critical(e)
398
- logger.critical(traceback.format_exc())
399
  pass
400
 
401
  def get_data(self):
@@ -452,19 +282,8 @@ class Multitargeting(QtWidgets.QMainWindow):
452
  self.cspr_file = self.organisms_to_files[str(self.organism_drop.currentText())][endos[0]][0]
453
  self.db_file = self.organisms_to_files[str(self.organism_drop.currentText())][endos[0]][1]
454
  except Exception as e:
455
- logger.critical("Error in get_data() in multi-targeting.")
456
- logger.critical(e)
457
- logger.critical(traceback.format_exc())
458
- msgBox = QtWidgets.QMessageBox()
459
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
460
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
461
- msgBox.setWindowTitle("Fatal Error")
462
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
463
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
464
- msgBox.exec()
465
-
466
- exit(-1)
467
-
468
  def update_endos(self):
469
  try:
470
  #try to disconnect index changed signal on endo dropdown if there is one
@@ -478,26 +297,15 @@ class Multitargeting(QtWidgets.QMainWindow):
478
  endos = self.organisms_to_endos[str(self.organism_drop.currentText())]
479
  self.endo_drop.addItems(endos)
480
  except Exception as e:
481
- logger.critical("Error in update_endos() in multi-targeting.")
482
- logger.critical(e)
483
- logger.critical(traceback.format_exc())
484
- msgBox = QtWidgets.QMessageBox()
485
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
486
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
487
- msgBox.setWindowTitle("Fatal Error")
488
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
489
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
490
- msgBox.exec()
491
-
492
- exit(-1)
493
-
494
  def make_graphs(self):
495
  try:
496
  self.cspr_file = self.organisms_to_files[str(self.organism_drop.currentText())][str(self.endo_drop.currentText())][0]
497
  self.db_file = self.organisms_to_files[str(self.organism_drop.currentText())][str(self.endo_drop.currentText())][1]
498
 
499
  self.loading_window.loading_bar.setValue(0)
500
- self.loading_window.centerUI()
501
  self.loading_window.show()
502
  QtCore.QCoreApplication.processEvents()
503
  self.chromo_length.clear()
@@ -521,18 +329,7 @@ class Multitargeting(QtWidgets.QMainWindow):
521
  QtWidgets.QApplication.processEvents()
522
 
523
  except Exception as e:
524
- logger.critical("Error in make_graphs() in multi-targeting.")
525
- logger.critical(e)
526
- logger.critical(traceback.format_exc())
527
- msgBox = QtWidgets.QMessageBox()
528
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
529
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
530
- msgBox.setWindowTitle("Fatal Error")
531
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
532
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
533
- msgBox.exec()
534
-
535
- exit(-1)
536
 
537
  #function to fill table in UI
538
  def fill_table(self):
@@ -676,18 +473,7 @@ class Multitargeting(QtWidgets.QMainWindow):
676
  #reconnect row trigger
677
  self.table.itemSelectionChanged.connect(self.row_selection_trigger)
678
  except Exception as e:
679
- logger.critical("Error in fill_table() in multi-targeting.")
680
- logger.critical(e)
681
- logger.critical(traceback.format_exc())
682
- msgBox = QtWidgets.QMessageBox()
683
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
684
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
685
- msgBox.setWindowTitle("Fatal Error")
686
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
687
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
688
- msgBox.exec()
689
-
690
- exit(-1)
691
 
692
  #function for triggering graph updates when user selects row in table
693
  def row_selection_trigger(self):
@@ -699,19 +485,8 @@ class Multitargeting(QtWidgets.QMainWindow):
699
  self.fill_Chromo_Text(seed)
700
  self.chro_bar_create(seed)
701
  except Exception as e:
702
- logger.critical("Error in row_selection_trigger() in multi-targeting.")
703
- logger.critical(e)
704
- logger.critical(traceback.format_exc())
705
- msgBox = QtWidgets.QMessageBox()
706
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
707
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
708
- msgBox.setWindowTitle("Fatal Error")
709
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
710
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
711
- msgBox.exec()
712
-
713
- exit(-1)
714
-
715
  # sorting to table
716
  def table_sorting(self, logicalIndex):
717
  try:
@@ -721,18 +496,7 @@ class Multitargeting(QtWidgets.QMainWindow):
721
  else:
722
  self.table.sortItems(logicalIndex, QtCore.Qt.AscendingOrder)
723
  except Exception as e:
724
- logger.critical("Error in table_sorting() in multi-targeting.")
725
- logger.critical(e)
726
- logger.critical(traceback.format_exc())
727
- msgBox = QtWidgets.QMessageBox()
728
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
729
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
730
- msgBox.setWindowTitle("Fatal Error")
731
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
732
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
733
- msgBox.exec()
734
-
735
- exit(-1)
736
 
737
  #fill in chromo bar visualization
738
  def fill_Chromo_Text(self, seed):
@@ -844,18 +608,7 @@ class Multitargeting(QtWidgets.QMainWindow):
844
 
845
  return False
846
  except Exception as e:
847
- logger.critical("Error in fill_Chromo_text() in multi-targeting.")
848
- logger.critical(e)
849
- logger.critical(traceback.format_exc())
850
- msgBox = QtWidgets.QMessageBox()
851
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
852
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
853
- msgBox.setWindowTitle("Fatal Error")
854
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
855
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
856
- msgBox.exec()
857
-
858
- exit(-1)
859
 
860
  # creates bar graph num of repeats vs. chromosome
861
  def chro_bar_create(self, seed):
@@ -898,18 +651,7 @@ class Multitargeting(QtWidgets.QMainWindow):
898
  self.line_canvas.axes.tick_params(axis='both', which='major', labelsize=8)
899
  self.line_canvas.draw()
900
  except Exception as e:
901
- logger.critical("Error in chro_bar_create() in multi-targeting.")
902
- logger.critical(e)
903
- logger.critical(traceback.format_exc())
904
- msgBox = QtWidgets.QMessageBox()
905
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
906
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
907
- msgBox.setWindowTitle("Fatal Error")
908
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
909
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
910
- msgBox.exec()
911
-
912
- exit(-1)
913
 
914
  def bar_seeds_vs_repeats(self):
915
  try:
@@ -943,19 +685,8 @@ class Multitargeting(QtWidgets.QMainWindow):
943
 
944
  self.bar_bool = True
945
  except Exception as e:
946
- logger.critical("Error in bar_seeds_vs_repeats() in multi-targeting.")
947
- logger.critical(e)
948
- logger.critical(traceback.format_exc())
949
- msgBox = QtWidgets.QMessageBox()
950
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
951
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
952
- msgBox.setWindowTitle("Fatal Error")
953
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
954
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
955
- msgBox.exec()
956
-
957
- exit(-1)
958
-
959
  # # plots the repeats per ID number graph as line graph
960
  def plot_repeats_vs_seeds(self):
961
  try:
@@ -994,19 +725,8 @@ class Multitargeting(QtWidgets.QMainWindow):
994
  self.line_bool = True
995
  self.line_canvas.draw()
996
  except Exception as e:
997
- logger.critical("Error in plot_repeats_vs_seeds() in multi-targeting.")
998
- logger.critical(e)
999
- logger.critical(traceback.format_exc())
1000
- msgBox = QtWidgets.QMessageBox()
1001
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1002
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1003
- msgBox.setWindowTitle("Fatal Error")
1004
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1005
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1006
- msgBox.exec()
1007
-
1008
- exit(-1)
1009
-
1010
  def seed_chromo_changed(self):
1011
  try:
1012
  self.loading_window.loading_bar.setValue(5)
@@ -1017,19 +737,8 @@ class Multitargeting(QtWidgets.QMainWindow):
1017
  self.loading_window.loading_bar.setValue(100)
1018
  self.loading_window.hide()
1019
  except Exception as e:
1020
- logger.critical("Error in seed_chromo_changed() in multi-targeting.")
1021
- logger.critical(e)
1022
- logger.critical(traceback.format_exc())
1023
- msgBox = QtWidgets.QMessageBox()
1024
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1025
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1026
- msgBox.setWindowTitle("Fatal Error")
1027
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1028
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1029
- msgBox.exec()
1030
-
1031
- exit(-1)
1032
-
1033
  #connects to go back button in bottom left to switch back to the main CASPER window
1034
  def go_back(self):
1035
  try:
@@ -1038,19 +747,8 @@ class Multitargeting(QtWidgets.QMainWindow):
1038
  GlobalSettings.mainWindow.show()
1039
  self.hide()
1040
  except Exception as e:
1041
- logger.critical("Error in go_back() in multi-targeting.")
1042
- logger.critical(e)
1043
- logger.critical(traceback.format_exc())
1044
- msgBox = QtWidgets.QMessageBox()
1045
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1046
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1047
- msgBox.setWindowTitle("Fatal Error")
1048
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1049
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1050
- msgBox.exec()
1051
-
1052
- exit(-1)
1053
-
1054
  # this function calls the closingWindow class.
1055
  def closeEvent(self, event):
1056
  try:
@@ -1059,132 +757,22 @@ class Multitargeting(QtWidgets.QMainWindow):
1059
  self.multitargeting_statistics.hide()
1060
  event.accept()
1061
  except Exception as e:
1062
- logger.critical("Error in closeEvent() in multi-targeting.")
1063
- logger.critical(e)
1064
- logger.critical(traceback.format_exc())
1065
- msgBox = QtWidgets.QMessageBox()
1066
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1067
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1068
- msgBox.setWindowTitle("Fatal Error")
1069
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1070
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1071
- msgBox.exec()
1072
-
1073
- exit(-1)
1074
-
1075
 
1076
  class loading_window(QtWidgets.QMainWindow):
1077
  def __init__(self):
1078
  try:
1079
  super(loading_window, self).__init__()
1080
- uic.loadUi(GlobalSettings.appdir + "loading_data_form.ui", self)
1081
  self.setWindowIcon(Qt.QIcon(GlobalSettings.appdir + "cas9image.ico"))
1082
  self.loading_bar.setValue(0)
1083
  self.setWindowTitle("Loading Data")
1084
 
1085
- #scale UI
1086
- self.scaleUI()
1087
-
1088
- except Exception as e:
1089
- logger.critical("Error initializing loading_window class in multi-targeting.")
1090
- logger.critical(e)
1091
- logger.critical(traceback.format_exc())
1092
- msgBox = QtWidgets.QMessageBox()
1093
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1094
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1095
- msgBox.setWindowTitle("Fatal Error")
1096
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1097
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1098
- msgBox.exec()
1099
-
1100
- exit(-1)
1101
-
1102
- def scaleUI(self):
1103
- try:
1104
- self.repaint()
1105
- QtWidgets.QApplication.processEvents()
1106
-
1107
- screen = self.screen()
1108
- dpi = screen.physicalDotsPerInch()
1109
- width = screen.geometry().width()
1110
- height = screen.geometry().height()
1111
-
1112
- # font scaling
1113
- # 14px is used for 92 dpi
1114
- fontSize = 12
1115
- self.centralWidget().setStyleSheet("font: " + str(fontSize) + "pt 'Arial';")
1116
 
1117
- self.adjustSize()
1118
-
1119
- currentWidth = self.size().width()
1120
- currentHeight = self.size().height()
1121
-
1122
- # scale/center window
1123
- scaledWidth = int((width * 450) / 1920)
1124
- scaledHeight = int((height * 125) / 1080)
1125
-
1126
- if scaledHeight < currentHeight:
1127
- scaledHeight = currentHeight
1128
- if scaledWidth < currentWidth:
1129
- scaledWidth = currentWidth
1130
-
1131
- screen = QtWidgets.QApplication.desktop().screenNumber(QtWidgets.QApplication.desktop().cursor().pos())
1132
- centerPoint = QtWidgets.QApplication.desktop().screenGeometry(screen).center()
1133
- x = centerPoint.x()
1134
- y = centerPoint.y()
1135
- x = x - (math.ceil(scaledWidth / 2))
1136
- y = y - (math.ceil(scaledHeight / 2))
1137
- self.setGeometry(x, y, scaledWidth, scaledHeight)
1138
-
1139
- self.repaint()
1140
- QtWidgets.QApplication.processEvents()
1141
  except Exception as e:
1142
- logger.critical("Error in scaleUI() in loading_window() class in multitargeting.")
1143
- logger.critical(e)
1144
- logger.critical(traceback.format_exc())
1145
- msgBox = QtWidgets.QMessageBox()
1146
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1147
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1148
- msgBox.setWindowTitle("Fatal Error")
1149
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1150
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1151
- msgBox.exec()
1152
-
1153
- exit(-1)
1154
-
1155
- def centerUI(self):
1156
- try:
1157
- self.repaint()
1158
- QtWidgets.QApplication.processEvents()
1159
-
1160
- # center window on current screen
1161
- width = self.width()
1162
- height = self.height()
1163
- screen = QtWidgets.QApplication.desktop().screenNumber(QtWidgets.QApplication.desktop().cursor().pos())
1164
- centerPoint = QtWidgets.QApplication.desktop().screenGeometry(screen).center()
1165
- x = centerPoint.x()
1166
- y = centerPoint.y()
1167
- x = x - (math.ceil(width / 2))
1168
- y = y - (math.ceil(height / 2))
1169
- self.setGeometry(x, y, width, height)
1170
-
1171
- self.repaint()
1172
- QtWidgets.QApplication.processEvents()
1173
- except Exception as e:
1174
- logger.critical("Error in centerUI() in loading window in multitargeting.")
1175
- logger.critical(e)
1176
- logger.critical(traceback.format_exc())
1177
- msgBox = QtWidgets.QMessageBox()
1178
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1179
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1180
- msgBox.setWindowTitle("Fatal Error")
1181
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1182
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1183
- msgBox.exec()
1184
-
1185
- exit(-1)
1186
-
1187
-
1188
  class MplCanvas(FigureCanvasQTAgg):
1189
  def __init__(self, parent=None, width=5, height=4, dpi=100):
1190
  try:
@@ -1207,252 +795,31 @@ class MplCanvas(FigureCanvasQTAgg):
1207
  # QtWidgets.QSizePolicy.Expanding)
1208
  # FigureCanvasQTAgg.updateGeometry(self)
1209
  except Exception as e:
1210
- logger.critical("Error initializing MplCanvas class in multi-targeting.")
1211
- logger.critical(e)
1212
- logger.critical(traceback.format_exc())
1213
- msgBox = QtWidgets.QMessageBox()
1214
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1215
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1216
- msgBox.setWindowTitle("Fatal Error")
1217
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1218
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1219
- msgBox.exec()
1220
-
1221
- exit(-1)
1222
-
1223
 
1224
  class Multitargeting_Statistics(QtWidgets.QMainWindow):
1225
  def __init__(self, parent=None):
1226
  try:
1227
  super(Multitargeting_Statistics, self).__init__(parent)
1228
- uic.loadUi(GlobalSettings.appdir + 'multitargeting_stats.ui', self)
1229
  self.setWindowIcon(Qt.QIcon(GlobalSettings.appdir + "cas9image.ico"))
1230
  self.setWindowTitle("Statistics")
1231
 
1232
- #scale UI
1233
- self.scaleUI()
1234
-
1235
- except Exception as e:
1236
- logger.critical("Error initializing Multitargeting_Statistics class in multi-targeting.")
1237
- logger.critical(e)
1238
- logger.critical(traceback.format_exc())
1239
- msgBox = QtWidgets.QMessageBox()
1240
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1241
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1242
- msgBox.setWindowTitle("Fatal Error")
1243
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1244
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1245
- msgBox.exec()
1246
-
1247
- exit(-1)
1248
-
1249
- #scale UI based on current screen
1250
- def scaleUI(self):
1251
- try:
1252
- self.repaint()
1253
- QtWidgets.QApplication.processEvents()
1254
-
1255
- screen = self.screen()
1256
- dpi = screen.physicalDotsPerInch()
1257
- width = screen.geometry().width()
1258
- height = screen.geometry().height()
1259
-
1260
- # font scaling
1261
- # 16px is used for 92 dpi / 1920x1080
1262
- fontSize = 12
1263
- self.fontSize = fontSize
1264
- self.centralWidget().setStyleSheet("font: " + str(fontSize) + "pt 'Arial';")
1265
-
1266
- # CASPER header scaling
1267
- fontSize = 20
1268
- self.title.setStyleSheet("font: bold " + str(fontSize) + "pt 'Arial';")
1269
-
1270
- self.adjustSize()
1271
-
1272
- currentWidth = self.size().width()
1273
- currentHeight = self.size().height()
1274
-
1275
- # window scaling
1276
- scaledWidth = int((width * 275) / 1920)
1277
- scaledHeight = int((height * 185) / 1080)
1278
-
1279
- if scaledHeight < currentHeight:
1280
- scaledHeight = currentHeight
1281
- if scaledWidth < currentWidth:
1282
- scaledWidth = currentWidth
1283
-
1284
- screen = QtWidgets.QApplication.desktop().screenNumber(QtWidgets.QApplication.desktop().cursor().pos())
1285
- centerPoint = QtWidgets.QApplication.desktop().screenGeometry(screen).center()
1286
- x = centerPoint.x()
1287
- y = centerPoint.y()
1288
- x = x - (math.ceil(scaledWidth / 2))
1289
- y = y - (math.ceil(scaledHeight / 2))
1290
- self.setGeometry(x, y, scaledWidth, scaledHeight)
1291
-
1292
- self.repaint()
1293
- QtWidgets.QApplication.processEvents()
1294
- except Exception as e:
1295
- logger.critical("Error in scaleUI() in multitargeting statistics in multitargeting.")
1296
- logger.critical(e)
1297
- logger.critical(traceback.format_exc())
1298
- msgBox = QtWidgets.QMessageBox()
1299
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1300
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1301
- msgBox.setWindowTitle("Fatal Error")
1302
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1303
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1304
- msgBox.exec()
1305
-
1306
- exit(-1)
1307
-
1308
- # center UI on current screen
1309
- def centerUI(self):
1310
- try:
1311
- self.repaint()
1312
- QtWidgets.QApplication.processEvents()
1313
-
1314
- # center window on current screen
1315
- width = self.width()
1316
- height = self.height()
1317
- screen = QtWidgets.QApplication.desktop().screenNumber(QtWidgets.QApplication.desktop().cursor().pos())
1318
- centerPoint = QtWidgets.QApplication.desktop().screenGeometry(screen).center()
1319
- x = centerPoint.x()
1320
- y = centerPoint.y()
1321
- x = x - (math.ceil(width / 2))
1322
- y = y - (math.ceil(height / 2))
1323
- self.setGeometry(x, y, width, height)
1324
 
1325
- self.repaint()
1326
- QtWidgets.QApplication.processEvents()
1327
  except Exception as e:
1328
- logger.critical("Error in centerUI() in multitargeting statistics in multitargeting.")
1329
- logger.critical(e)
1330
- logger.critical(traceback.format_exc())
1331
- msgBox = QtWidgets.QMessageBox()
1332
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1333
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1334
- msgBox.setWindowTitle("Fatal Error")
1335
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1336
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1337
- msgBox.exec()
1338
-
1339
- exit(-1)
1340
-
1341
-
1342
  class sql_query_settings(QtWidgets.QMainWindow):
1343
  def __init__(self):
1344
  try:
1345
  super(sql_query_settings, self).__init__()
1346
- uic.loadUi(GlobalSettings.appdir + "multitargeting_sql_settings.ui", self)
1347
  self.setWindowTitle("SQL Settings")
1348
  self.setWindowIcon(Qt.QIcon(GlobalSettings.appdir + "cas9image.ico"))
1349
  self.row_count.setValidator(QtGui.QIntValidator())
1350
 
1351
- #scale the UI
1352
- self.scaleUI()
1353
-
1354
- except Exception as e:
1355
- logger.critical("Error initializing sql_query_settings class in multi-targeting.")
1356
- logger.critical(e)
1357
- logger.critical(traceback.format_exc())
1358
- msgBox = QtWidgets.QMessageBox()
1359
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1360
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1361
- msgBox.setWindowTitle("Fatal Error")
1362
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1363
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1364
- msgBox.exec()
1365
-
1366
- exit(-1)
1367
-
1368
- #scale UI based on current screen
1369
- def scaleUI(self):
1370
- try:
1371
- self.repaint()
1372
- QtWidgets.QApplication.processEvents()
1373
-
1374
- screen = self.screen()
1375
- dpi = screen.physicalDotsPerInch()
1376
- width = screen.geometry().width()
1377
- height = screen.geometry().height()
1378
-
1379
- # font scaling
1380
- # 16px is used for 92 dpi / 1920x1080
1381
- fontSize = 12
1382
- self.fontSize = fontSize
1383
- self.centralWidget().setStyleSheet("font: " + str(fontSize) + "pt 'Arial';")
1384
-
1385
- # CASPER header scaling
1386
- fontSize = 20
1387
- self.title.setStyleSheet("font: bold " + str(fontSize) + "pt 'Arial';")
1388
 
1389
- self.adjustSize()
1390
-
1391
- currentWidth = self.size().width()
1392
- currentHeight = self.size().height()
1393
-
1394
- # window scaling
1395
- scaledWidth = int((width * 375) / 1920)
1396
- scaledHeight = int((height * 140) / 1080)
1397
-
1398
- if scaledHeight < currentHeight:
1399
- scaledHeight = currentHeight
1400
- if scaledWidth < currentWidth:
1401
- scaledWidth = currentWidth
1402
-
1403
- screen = QtWidgets.QApplication.desktop().screenNumber(QtWidgets.QApplication.desktop().cursor().pos())
1404
- centerPoint = QtWidgets.QApplication.desktop().screenGeometry(screen).center()
1405
- x = centerPoint.x()
1406
- y = centerPoint.y()
1407
- x = x - (math.ceil(scaledWidth / 2))
1408
- y = y - (math.ceil(scaledHeight / 2))
1409
- self.setGeometry(x, y, scaledWidth, scaledHeight)
1410
-
1411
- self.repaint()
1412
- QtWidgets.QApplication.processEvents()
1413
- except Exception as e:
1414
- logger.critical("Error in scaleUI() in sql settings in multitargeting.")
1415
- logger.critical(e)
1416
- logger.critical(traceback.format_exc())
1417
- msgBox = QtWidgets.QMessageBox()
1418
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1419
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1420
- msgBox.setWindowTitle("Fatal Error")
1421
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1422
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1423
- msgBox.exec()
1424
-
1425
- exit(-1)
1426
-
1427
- #center UI on current screen
1428
- def centerUI(self):
1429
- try:
1430
- self.repaint()
1431
- QtWidgets.QApplication.processEvents()
1432
-
1433
- # center window on current screen
1434
- width = self.width()
1435
- height = self.height()
1436
- screen = QtWidgets.QApplication.desktop().screenNumber(QtWidgets.QApplication.desktop().cursor().pos())
1437
- centerPoint = QtWidgets.QApplication.desktop().screenGeometry(screen).center()
1438
- x = centerPoint.x()
1439
- y = centerPoint.y()
1440
- x = x - (math.ceil(width / 2))
1441
- y = y - (math.ceil(height / 2))
1442
- self.setGeometry(x, y, width, height)
1443
-
1444
- self.repaint()
1445
- QtWidgets.QApplication.processEvents()
1446
  except Exception as e:
1447
- logger.critical("Error in centerUI() in sql settings in multitargeting.")
1448
- logger.critical(e)
1449
- logger.critical(traceback.format_exc())
1450
- msgBox = QtWidgets.QMessageBox()
1451
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1452
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1453
- msgBox.setWindowTitle("Fatal Error")
1454
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1455
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1456
- msgBox.exec()
1457
-
1458
- exit(-1)
 
1
  from PyQt5 import QtWidgets, Qt, QtGui, QtCore, uic
2
  from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
3
  from matplotlib.figure import Figure
4
+ import models.GlobalSettings as GlobalSettings
5
  import matplotlib
6
+ from utils.Algorithms import SeqTranslate
7
+ from models.CSPRparser import CSPRparser
8
  from matplotlib.ticker import MaxNLocator
9
  import os
10
  import sqlite3
 
18
  from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
19
  from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
20
  import matplotlib.pyplot as plt
21
+ from utils.ui import show_message, show_error, scale_ui, center_ui
22
+
23
  logger = GlobalSettings.logger
24
 
25
  class Multitargeting(QtWidgets.QMainWindow):
 
27
  try:
28
  self.count = 0
29
  super(Multitargeting, self).__init__()
30
+ uic.loadUi(GlobalSettings.appdir + 'ui/mt.ui', self)
31
  self.setWindowIcon(Qt.QIcon(GlobalSettings.appdir + "cas9image.ico"))
32
  self.multitargeting_statistics = Multitargeting_Statistics()
33
 
 
85
  self.selectAll.stateChanged.connect(self.select_all)
86
  self.selectAll.setEnabled(False)
87
 
88
+ # go back to main window
89
  self.back_button.clicked.connect(self.go_back)
90
 
91
  # Statistics storage variables
 
98
  self.bar_coords = []
99
  self.seed_id_seq_pair = {}
100
 
 
101
  self.parser = CSPRparser("")
102
 
103
  self.ready_chromo_min_max = True
104
  self.ready_chromo_make_graph = True
105
  self.info_path = os.getcwd()
106
 
 
107
  self.scene = QtWidgets.QGraphicsScene()
108
  self.scene2 = QtWidgets.QGraphicsScene()
109
  self.graphicsView_2.setScene(self.scene2)
 
118
  self.sql_settings = sql_query_settings()
119
  self.sql_settings.row_count.textChanged.connect(self.sql_row_count_value_changed)
120
 
 
121
  self.first_show = True
122
+ scale_ui(self, custom_scale_width=1400, custom_scale_height=900)
123
+
124
+ dpi = self.screen().physicalDotsPerInch()
125
+ self.dpi = dpi
126
 
127
  except Exception as e:
128
+ show_error("Error initializing Multi-targeting.", e)
 
 
 
 
 
 
 
 
 
 
129
 
130
  def select_all(self):
131
  try:
 
134
  else:
135
  self.table.clearSelection()
136
  except Exception as e:
137
+ show_error("Error in selectAll() in multitargeting.", e)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
 
139
  def export_tool(self):
140
  try:
141
  select_items = self.table.selectedItems()
142
  if len(select_items) <= 0:
143
+ show_message(
144
+ fontSize=12,
145
+ icon=QtWidgets.QMessageBox.Icon.Critical,
146
+ title="No targets were highlighted",
147
+ message="Please highlight the targets you want to be exported to a CSV File!"
148
+ )
 
 
149
  return
150
  GlobalSettings.mainWindow.export_tool_window.launch(select_items,"mt")
151
  except Exception as e:
152
+ show_error("Error in export_tool() in multi-targeting.", e)
 
 
 
 
 
 
 
 
 
 
 
153
 
154
  def show_statistics(self):
155
  try:
156
  if (self.line_bool and self.bar_bool):
157
+ center_ui(self.multitargeting_statistics)
158
  self.multitargeting_statistics.show()
159
  self.multitargeting_statistics.activateWindow()
160
  else:
161
+ show_message(
162
+ fontSize=12,
163
+ icon=QtWidgets.QMessageBox.Icon.Critical,
164
+ title="No analysis run.",
165
+ message="Multitargeting Analysis must be performed before viewing statistics.\n\nSelect an organism and endonuclease and click 'Analyze' then try again."
166
+ )
 
 
167
  return True
168
  except Exception as e:
169
+ show_error("Error in show_statistics() in multi-targeting.", e)
 
 
 
 
 
 
 
 
 
 
 
170
 
171
  #event handler to show details of targets in chromosome viewer while hovering over canvases
172
  def chromosome_event_handler(self, event):
 
203
  text.setFont(font)
204
 
205
  except Exception as e:
206
+ show_error("Error in event_data() in multi-targeting.", e)
 
 
 
 
 
 
 
 
 
 
 
207
 
208
  def launch(self):
209
  try:
210
  self.get_data()
211
  except Exception as e:
212
+ show_error("Error in launch() in multi-targeting.", e)
213
+
 
 
 
 
 
 
 
 
 
 
 
214
  #button trigger for sql settings
215
  def update_sql_query_settings(self):
216
  try:
217
+ center_ui(self.sql_settings)
218
  self.sql_settings.show()
219
  self.sql_settings.activateWindow()
220
  except Exception as e:
221
+ show_error("Error in update_sql_query_settings() in multi-targeting.", e)
222
+
 
 
 
 
 
 
 
 
 
 
 
223
  #trigger for if sql line edit value has changed
224
  def sql_row_count_value_changed(self):
225
  try:
226
  self.row_limit = int(self.sql_settings.row_count.text())
227
  except Exception as e:
228
+ show_error("Error in sql_row_count_value_changed() in multi-targeting.", e)
 
 
229
  pass
230
 
231
  def get_data(self):
 
282
  self.cspr_file = self.organisms_to_files[str(self.organism_drop.currentText())][endos[0]][0]
283
  self.db_file = self.organisms_to_files[str(self.organism_drop.currentText())][endos[0]][1]
284
  except Exception as e:
285
+ show_error("Error in get_data() in multi-targeting.", e)
286
+
 
 
 
 
 
 
 
 
 
 
 
287
  def update_endos(self):
288
  try:
289
  #try to disconnect index changed signal on endo dropdown if there is one
 
297
  endos = self.organisms_to_endos[str(self.organism_drop.currentText())]
298
  self.endo_drop.addItems(endos)
299
  except Exception as e:
300
+ show_error("Error in update_endos() in multi-targeting.", e)
301
+
 
 
 
 
 
 
 
 
 
 
 
302
  def make_graphs(self):
303
  try:
304
  self.cspr_file = self.organisms_to_files[str(self.organism_drop.currentText())][str(self.endo_drop.currentText())][0]
305
  self.db_file = self.organisms_to_files[str(self.organism_drop.currentText())][str(self.endo_drop.currentText())][1]
306
 
307
  self.loading_window.loading_bar.setValue(0)
308
+ center_ui(self.loading_window)
309
  self.loading_window.show()
310
  QtCore.QCoreApplication.processEvents()
311
  self.chromo_length.clear()
 
329
  QtWidgets.QApplication.processEvents()
330
 
331
  except Exception as e:
332
+ show_error("Error in make_graphs() in multi-targeting.", e)
 
 
 
 
 
 
 
 
 
 
 
333
 
334
  #function to fill table in UI
335
  def fill_table(self):
 
473
  #reconnect row trigger
474
  self.table.itemSelectionChanged.connect(self.row_selection_trigger)
475
  except Exception as e:
476
+ show_error("Error in fill_table() in multi-targeting.", e)
 
 
 
 
 
 
 
 
 
 
 
477
 
478
  #function for triggering graph updates when user selects row in table
479
  def row_selection_trigger(self):
 
485
  self.fill_Chromo_Text(seed)
486
  self.chro_bar_create(seed)
487
  except Exception as e:
488
+ show_error("Error in row_selection_trigger() in multi-targeting.", e)
489
+
 
 
 
 
 
 
 
 
 
 
 
490
  # sorting to table
491
  def table_sorting(self, logicalIndex):
492
  try:
 
496
  else:
497
  self.table.sortItems(logicalIndex, QtCore.Qt.AscendingOrder)
498
  except Exception as e:
499
+ show_error("Error in table_sorting() in multi-targeting.", e)
 
 
 
 
 
 
 
 
 
 
 
500
 
501
  #fill in chromo bar visualization
502
  def fill_Chromo_Text(self, seed):
 
608
 
609
  return False
610
  except Exception as e:
611
+ show_error("Error in fill_Chromo_text() in multi-targeting.", e)
 
 
 
 
 
 
 
 
 
 
 
612
 
613
  # creates bar graph num of repeats vs. chromosome
614
  def chro_bar_create(self, seed):
 
651
  self.line_canvas.axes.tick_params(axis='both', which='major', labelsize=8)
652
  self.line_canvas.draw()
653
  except Exception as e:
654
+ show_error("Error in chro_bar_create() in multi-targeting.", e)
 
 
 
 
 
 
 
 
 
 
 
655
 
656
  def bar_seeds_vs_repeats(self):
657
  try:
 
685
 
686
  self.bar_bool = True
687
  except Exception as e:
688
+ show_error("Error in bar_seeds_vs_repeats() in multi-targeting.", e)
689
+
 
 
 
 
 
 
 
 
 
 
 
690
  # # plots the repeats per ID number graph as line graph
691
  def plot_repeats_vs_seeds(self):
692
  try:
 
725
  self.line_bool = True
726
  self.line_canvas.draw()
727
  except Exception as e:
728
+ show_error("Error in plot_repeats_vs_seeds() in multi-targeting.", e)
729
+
 
 
 
 
 
 
 
 
 
 
 
730
  def seed_chromo_changed(self):
731
  try:
732
  self.loading_window.loading_bar.setValue(5)
 
737
  self.loading_window.loading_bar.setValue(100)
738
  self.loading_window.hide()
739
  except Exception as e:
740
+ show_error("Error in seed_chromo_changed() in multi-targeting.", e)
741
+
 
 
 
 
 
 
 
 
 
 
 
742
  #connects to go back button in bottom left to switch back to the main CASPER window
743
  def go_back(self):
744
  try:
 
747
  GlobalSettings.mainWindow.show()
748
  self.hide()
749
  except Exception as e:
750
+ show_error("Error in go_back() in multi-targeting.", e)
751
+
 
 
 
 
 
 
 
 
 
 
 
752
  # this function calls the closingWindow class.
753
  def closeEvent(self, event):
754
  try:
 
757
  self.multitargeting_statistics.hide()
758
  event.accept()
759
  except Exception as e:
760
+ show_error("Error in closeEvent() in multi-targeting.", e)
 
 
 
 
 
 
 
 
 
 
 
 
761
 
762
  class loading_window(QtWidgets.QMainWindow):
763
  def __init__(self):
764
  try:
765
  super(loading_window, self).__init__()
766
+ uic.loadUi(GlobalSettings.appdir + "ui/loading_data_form.ui", self)
767
  self.setWindowIcon(Qt.QIcon(GlobalSettings.appdir + "cas9image.ico"))
768
  self.loading_bar.setValue(0)
769
  self.setWindowTitle("Loading Data")
770
 
771
+ scale_ui(self, custom_scale_width=450, custom_scale_height=125)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
772
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
773
  except Exception as e:
774
+ show_error("Error initializing loading_window class in multi-targeting.", e)
775
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
776
  class MplCanvas(FigureCanvasQTAgg):
777
  def __init__(self, parent=None, width=5, height=4, dpi=100):
778
  try:
 
795
  # QtWidgets.QSizePolicy.Expanding)
796
  # FigureCanvasQTAgg.updateGeometry(self)
797
  except Exception as e:
798
+ show_error("Error initializing MplCanvas class in multi-targeting.", e)
 
 
 
 
 
 
 
 
 
 
 
 
799
 
800
  class Multitargeting_Statistics(QtWidgets.QMainWindow):
801
  def __init__(self, parent=None):
802
  try:
803
  super(Multitargeting_Statistics, self).__init__(parent)
804
+ uic.loadUi(GlobalSettings.appdir + 'ui/multitargeting_stats.ui', self)
805
  self.setWindowIcon(Qt.QIcon(GlobalSettings.appdir + "cas9image.ico"))
806
  self.setWindowTitle("Statistics")
807
 
808
+ scale_ui(self, custom_scale_width=275, custom_scale_height=185)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
809
 
 
 
810
  except Exception as e:
811
+ show_error("Error initializing Multitargeting_Statistics class in multi-targeting.", e)
812
+
 
 
 
 
 
 
 
 
 
 
 
 
813
  class sql_query_settings(QtWidgets.QMainWindow):
814
  def __init__(self):
815
  try:
816
  super(sql_query_settings, self).__init__()
817
+ uic.loadUi(GlobalSettings.appdir + "ui/multitargeting_sql_settings.ui", self)
818
  self.setWindowTitle("SQL Settings")
819
  self.setWindowIcon(Qt.QIcon(GlobalSettings.appdir + "cas9image.ico"))
820
  self.row_count.setValidator(QtGui.QIntValidator())
821
 
822
+ scale_ui(self, custom_scale_width=375, custom_scale_height=140)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
823
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
824
  except Exception as e:
825
+ show_error("Error initializing sql_query_settings class in multi-targeting.", e)
 
 
 
 
 
 
 
 
 
 
 
controllers/ncbi.py ADDED
@@ -0,0 +1,915 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from Bio import Entrez
2
+ from bs4 import BeautifulSoup
3
+ from PyQt5 import QtWidgets, Qt, QtCore, uic
4
+ from ftplib import FTP
5
+ import gzip
6
+ import pandas as pd
7
+ import shutil
8
+ import os, time
9
+ import ssl
10
+ import models.GlobalSettings as GlobalSettings
11
+ import platform
12
+ import traceback
13
+ import math
14
+ from utils.ui import show_message, show_error, scale_ui, center_ui
15
+
16
+ #global logger
17
+ logger = GlobalSettings.logger
18
+
19
+ ssl._create_default_https_context = ssl._create_unverified_context
20
+
21
+ Entrez.email = "casper2informatics@gmail.com"
22
+
23
+ #model for filtering columns in ncbi table
24
+ class CustomProxyModel(QtCore.QSortFilterProxyModel):
25
+ def __init__(self, parent=None):
26
+ print("Initializing CustomProxyModel")
27
+ try:
28
+ super().__init__(parent)
29
+ self._filters = dict()
30
+ except Exception as e:
31
+ show_error("Error initializing CustomProxyModel class in ncbi tool.", e)
32
+
33
+ @property
34
+ def filters(self):
35
+ try:
36
+ return self._filters
37
+ except Exception as e:
38
+ show_error("Error in filter() in custom proxy model in ncbi tool.", e)
39
+
40
+ def setFilter(self, expresion, column):
41
+ try:
42
+ if expresion:
43
+ self.filters[column] = expresion
44
+ elif column in self.filters:
45
+ del self.filters[column]
46
+ self.invalidateFilter()
47
+ except Exception as e:
48
+ show_error("Error in setFilters() in custom proxy model in ncbi tool.", e)
49
+
50
+ def filterAcceptsRow(self, source_row, source_parent):
51
+ try:
52
+ for column, expresion in self.filters.items():
53
+ text = self.sourceModel().index(source_row, column, source_parent).data()
54
+ regex = QtCore.QRegExp(
55
+ expresion, QtCore.Qt.CaseInsensitive, QtCore.QRegExp.RegExp
56
+ )
57
+ if regex.indexIn(text) == -1:
58
+ return False
59
+ return True
60
+
61
+ except Exception as e:
62
+ show_error("Error in filterAcceptsRow() in custom proxy model in ncbi tool.", e)
63
+
64
+ #model for the data in the ncbi search table
65
+ class PandasModel(QtCore.QAbstractTableModel):
66
+ def __init__(self, df=pd.DataFrame(), parent=None):
67
+ try:
68
+ QtCore.QAbstractTableModel.__init__(self, parent=parent)
69
+ self._df = df.copy()
70
+ except Exception as e:
71
+ show_error("Error initializing PandasModel class in ncbi tool.", e)
72
+
73
+ def toDataFrame(self):
74
+ try:
75
+ return self._df.copy()
76
+
77
+ except Exception as e:
78
+ show_error("Error in toDataFrame() in Pandas Model in ncbi tool.", e)
79
+
80
+ def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
81
+ try:
82
+ if role != QtCore.Qt.DisplayRole:
83
+ return QtCore.QVariant()
84
+
85
+ if orientation == QtCore.Qt.Horizontal:
86
+ try:
87
+ return self._df.columns.tolist()[section]
88
+ except (IndexError, ):
89
+ return QtCore.QVariant()
90
+ elif orientation == QtCore.Qt.Vertical:
91
+ try:
92
+ return self._df.index.tolist()[section]
93
+ except (IndexError, ):
94
+ return QtCore.QVariant()
95
+ except Exception as e:
96
+ show_error("Error in headerData() in Pandas Model in ncbi tool.", e)
97
+
98
+ def data(self, index, role=QtCore.Qt.DisplayRole):
99
+ try:
100
+ if role == QtCore.Qt.TextAlignmentRole:
101
+ return QtCore.Qt.AlignCenter
102
+ if role != QtCore.Qt.DisplayRole:
103
+ return QtCore.QVariant()
104
+ if not index.isValid():
105
+ return QtCore.QVariant()
106
+
107
+ return QtCore.QVariant(str(self._df.iloc[index.row(), index.column()]))
108
+
109
+ except Exception as e:
110
+ show_error("Error in data() in Pandas Model in ncbi tool.", e)
111
+
112
+ def setData(self, index, value, role):
113
+ try:
114
+ row = self._df.index[index.row()]
115
+ col = self._df.columns[index.column()]
116
+ if hasattr(value, 'toPyObject'):
117
+ # PyQt4 gets a QVariant
118
+ value = value.toPyObject()
119
+ else:
120
+ # PySide gets an unicode
121
+ dtype = self._df[col].dtype
122
+ if dtype != object:
123
+ value = None if value == '' else dtype.type(value)
124
+ self._df.set_value(row, col, value)
125
+ return True
126
+ except Exception as e:
127
+ show_error("Error in setData() in Pandas Model in ncbi tool.", e)
128
+
129
+ def rowCount(self, parent=QtCore.QModelIndex()):
130
+ try:
131
+ return len(self._df.index)
132
+
133
+ except Exception as e:
134
+ show_error("Error in rowCount() in Pandas Model in ncbi tool.", e)
135
+
136
+ def columnCount(self, parent=QtCore.QModelIndex()):
137
+ try:
138
+ return len(self._df.columns)
139
+ except Exception as e:
140
+ show_error("Error in columnCount() in Pandas Model in ncbi tool.", e)
141
+
142
+ def sort(self, column, order):
143
+ try:
144
+ colname = self._df.columns.tolist()[column]
145
+ self.layoutAboutToBeChanged.emit()
146
+ self._df.sort_values(colname, ascending=order, inplace=True)
147
+ self._df.reset_index(inplace=True, drop=True)
148
+ self.layoutChanged.emit()
149
+ except Exception as e:
150
+ show_error("Error in sort() in Pandas Model in ncbi tool.", e)
151
+
152
+ ## Taken from StackOverflow: https://stackoverflow.com/questions/57607072/update-pyqt-progress-from-another-thread-running-ftp-download
153
+ class DownloadThread(QtCore.QThread):
154
+ """ Overall signals """
155
+ finished = QtCore.pyqtSignal(object)
156
+ started = QtCore.pyqtSignal(object)
157
+
158
+ """ Download specific signals """
159
+ data_progress = QtCore.pyqtSignal(object) # This signal emits progress data for the progress bar
160
+ data_size = QtCore.pyqtSignal(object) # This signal emits the size of the file being downloaded
161
+ file_started = QtCore.pyqtSignal(object) # This signal emits when the file starts downloading
162
+ file_finished = QtCore.pyqtSignal(object) # This signal emits the name of the file downloaded
163
+
164
+ def __init__(self,parent,url,id):
165
+ try:
166
+ QtCore.QThread.__init__(self,parent)
167
+ self.id = id
168
+ self.url = url # Initialize URL
169
+ self.ftp = FTP('ftp.ncbi.nlm.nih.gov') # Initialize FTP object
170
+ self.ftp.login()
171
+ except Exception as e:
172
+ show_error("Error initializing Thread class in ncbi tool.", e)
173
+
174
+ def run(self):
175
+ try:
176
+ ### Start by making sure the url is valid
177
+ if self.url == "":
178
+ self.finished.emit((self.id, False))
179
+ return
180
+ else:
181
+ self.started.emit(self.id)
182
+ self.ftp.cwd(self.url) # Change to appropriate directory
183
+ dir_files = self.ftp.nlst() # Get list of files in directory
184
+ for file in dir_files: # Loop through every file in the directory
185
+ if GlobalSettings.mainWindow.ncbi.gbff_checkbox.isChecked(): # If a GBFF is supposed to be downloaded
186
+ if file.find('genomic.gbff') != -1: # If a GBFF exists in this directory
187
+
188
+ # check OS for output path
189
+ if platform.system() == "Windows":
190
+ output_file = GlobalSettings.CSPR_DB + "\\GBFF\\" + file
191
+ else:
192
+ output_file = GlobalSettings.CSPR_DB + "/GBFF/" + file
193
+
194
+ self.ftp.voidcmd('TYPE I')
195
+ totalsize = self.ftp.size(file) # Get size of file that is being downloaded
196
+ # The first signal sets the maximum for the progress bar
197
+ self.file_started.emit((self.id,'Downloading GBFF: ' + str(round(totalsize/1e6,2)) + 'MB...',str(totalsize))) # Emit that the file download is starting and size of file
198
+ with open(output_file, 'wb') as self.f:
199
+ self.ftp.retrbinary(f"RETR {file}", self.file_write) # Download the file, emitting progress as we go
200
+ self.decompress_file(output_file) # Decompress the file
201
+ self.file_finished.emit((self.id,'GBFF Downloaded!',output_file)) # Once download is finished, emit signal
202
+
203
+ if GlobalSettings.mainWindow.ncbi.fna_checkbox.isChecked(): # If a FNA is supposed to be downloaded
204
+ if file.find('genomic.fna') != -1 and file.find('_cds_') == -1 and file.find('_rna_') == -1: # If a FNA exists in this directory
205
+
206
+ # check OS for output path
207
+ if platform.system() == "Windows":
208
+ output_file = GlobalSettings.CSPR_DB + "\\FNA\\" + file
209
+ else:
210
+ output_file = GlobalSettings.CSPR_DB + "/FNA/" + file
211
+
212
+ self.ftp.voidcmd('TYPE I')
213
+ totalsize = self.ftp.size(file) # Get size of file that is being downloaded
214
+ self.file_started.emit((self.id,'Downloading FNA: ' + str(round(totalsize/1e6,2)) + 'MB...',str(totalsize))) # Emit that file download is starting
215
+ with open(output_file, 'wb') as self.f:
216
+ self.ftp.retrbinary(f"RETR {file}", self.file_write) # Download the file
217
+
218
+ self.decompress_file(output_file) # Decompress the file
219
+ self.file_finished.emit((self.id,'FNA Downloaded!',output_file)) # Once download is finished, emit signal
220
+ self.finished.emit((self.id,True))
221
+ self.ftp.quit() # Stop the FTP connection once everything has been downloaded
222
+ except Exception as e:
223
+ show_error("Error downloading file within DownloadThread class.", e)
224
+
225
+ def file_write(self, data):
226
+ self.f.write(data) # Write the downloaded data to a file
227
+ # The other signals increase a progress
228
+ self.data_progress.emit((self.id,str(len(data)))) # Emit a signal updating progress
229
+
230
+ def decompress_file(self, filename):
231
+ try:
232
+ block_size = 65536
233
+ with gzip.open(filename, 'rb') as f_in:
234
+ with open(str(filename).replace('.gz', ''), 'wb') as f_out:
235
+ while True:
236
+ block = f_in.read(block_size)
237
+ if not block:
238
+ break
239
+ else:
240
+ f_out.write(block)
241
+ os.remove(str(filename))
242
+ except Exception as e:
243
+ show_error("Error in decompress_file() in ncbi tool.", e)
244
+
245
+ class NCBI_search_tool(QtWidgets.QMainWindow):
246
+ def __init__(self):
247
+ try:
248
+ print("Initializing NCBI search tool...")
249
+ super(NCBI_search_tool, self).__init__()
250
+ uic.loadUi(GlobalSettings.appdir + 'ui/ncbi.ui', self)
251
+ self.setWindowIcon(Qt.QIcon(GlobalSettings.appdir + "cas9image.ico"))
252
+ self.setWindowTitle("NCBI Download Tool")
253
+ self.logicalIndex = 0
254
+ self.filters = dict()
255
+ self.download_button.clicked.connect(self.download_files_wrapper)
256
+ self.search_button.clicked.connect(self.query_db)
257
+ self.ncbi_table.verticalHeader().hide()
258
+ self.all_rows.clicked.connect(self.select_all)
259
+ self.back_button.clicked.connect(self.go_back)
260
+ self.ncbi_table.setFocusPolicy(QtCore.Qt.NoFocus)
261
+ self.progressBar.setValue(0)
262
+ self.rename_window = rename_window()
263
+ self.rename_window.submit_button.clicked.connect(self.submit_rename)
264
+ self.rename_window.go_back.clicked.connect(self.rename_go_back)
265
+ self.df = pd.DataFrame()
266
+ groupbox_style = """
267
+ QGroupBox:title{subcontrol-origin: margin;
268
+ left: 10px;
269
+ padding: 0 5px 0 5px;}
270
+ QGroupBox#Step1{border: 2px solid rgb(111,181,110);
271
+ border-radius: 9px;
272
+ font: bold 14pt 'Arial';
273
+ margin-top: 10px;}"""
274
+ self.Step1.setStyleSheet(groupbox_style)
275
+ self.Step2.setStyleSheet(groupbox_style.replace("Step1","Step2"))
276
+ self.Step3.setStyleSheet(groupbox_style.replace("Step1","Step3"))
277
+ self.ncbi_table.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
278
+ self.ncbi_table.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
279
+ #navigation page
280
+ self.goToPrompt = goToPrompt()
281
+ self.goToPrompt.stay.clicked.connect(self.stay)
282
+ self.goToPrompt.close.clicked.connect(self.close)
283
+
284
+ #loading label
285
+ self.loading_window = loading_window()
286
+
287
+ self.genbank_checkbox.toggled.connect(self.check_genbank)
288
+
289
+ self.first_show = True
290
+ scale_ui(self, custom_scale_width=1000, custom_scale_height=750)
291
+ self.fontSize = 12
292
+ except Exception as e:
293
+ show_error("Error initializing NCBI_search_tool class.", e)
294
+
295
+ def check_genbank(self):
296
+ if self.genbank_checkbox.isChecked():
297
+ show_message(
298
+ font_size=12,
299
+ title="Warning!",
300
+ message="The GenBank collection may contain poorly or partially annotated annotation files. We highly recommend using the RefSeq collection if it is available.",
301
+ icon=QtWidgets.QMessageBox.Icon.Warning
302
+ )
303
+ else:
304
+ pass
305
+
306
+ def go_back(self):
307
+ try:
308
+ """ Clear table """
309
+ self.df = pd.DataFrame() ###Make empty DF
310
+ self.model = PandasModel(self.df)
311
+ self.proxy = CustomProxyModel(self)
312
+ self.proxy.setSourceModel(self.model)
313
+ self.ncbi_table.setModel(self.proxy)
314
+ self.ncbi_table.verticalHeader().hide()
315
+ """ Clear all line edits """
316
+ self.organism_line_edit.clear()
317
+ self.infra_name_line_edit.clear()
318
+ self.ret_max_line_edit.setText("100")
319
+ self.infra_name_line_edit.clear()
320
+ """ Reset all checkboxes """
321
+ self.yes_box.setChecked(False)
322
+ self.genbank_checkbox.setChecked(False)
323
+ self.refseq_checkbox.setChecked(False)
324
+ self.gbff_checkbox.setChecked(False)
325
+ self.fna_checkbox.setChecked(False)
326
+ """ Hide window """
327
+ self.close()
328
+ except Exception as e:
329
+ show_error("Error in go_back() in ncbi tool.", e)
330
+
331
+ @QtCore.pyqtSlot()
332
+ def query_db(self):
333
+ try:
334
+ self.loading_window.loading_bar.setValue(5)
335
+ center_ui(self.loading_window)
336
+ self.loading_window.show()
337
+ QtCore.QCoreApplication.processEvents()
338
+
339
+ #setup table
340
+ self.comboBox = QtWidgets.QComboBox(self)
341
+ self.horizontalHeader = self.ncbi_table.horizontalHeader()
342
+ self.horizontalHeader.sectionClicked.connect(self.on_view_horizontalHeader_sectionClicked)
343
+
344
+ #Build Query commands
345
+ retmax = int(self.ret_max_line_edit.text())
346
+ if retmax == "":
347
+ retmax = 100
348
+ org = self.organism_line_edit.text()
349
+ term = '"' + org + '"[Organism]'
350
+ if self.yes_box.isChecked():
351
+ term += ' AND "Complete Genome"[Assembly Level]'
352
+ if self.infra_name_line_edit.text() != "":
353
+ term += ' AND "' + self.infra_name_line_edit.text() + '"[Infraspecific name]'
354
+
355
+ #Search DB for IDs
356
+ handle = Entrez.esearch(db="assembly", retmax=retmax, term=term)
357
+ content = handle.readlines()
358
+ content = "".join(str(content))
359
+ #bs_content = BeautifulSoup(content, "lxml")
360
+ bs_content = BeautifulSoup(content, "html.parser")
361
+
362
+ self.loading_window.loading_bar.setValue(20)
363
+ QtCore.QCoreApplication.processEvents()
364
+
365
+ #Extract IDs
366
+ idlist = bs_content.find('idlist')
367
+ ids = idlist.find_all('id')
368
+ ids = [i.text for i in ids]
369
+
370
+ self.loading_window.loading_bar.setValue(35)
371
+ QtCore.QCoreApplication.processEvents()
372
+
373
+ # Get Details on IDs
374
+ handle = Entrez.esummary(db="assembly", id=','.join(ids))
375
+ content = handle.readlines()
376
+ handle.close()
377
+ content = "".join(str(content))
378
+ #bs_content = BeautifulSoup(content, 'lxml')
379
+ bs_content = BeautifulSoup(content, "html.parser")
380
+
381
+ self.loading_window.loading_bar.setValue(55)
382
+
383
+ QtCore.QCoreApplication.processEvents()
384
+
385
+ #Prep Data for Table
386
+ assembly_name = bs_content.find_all('assemblyname')
387
+ genbank_ids = bs_content.find_all('genbank')
388
+ refseq_ids = bs_content.find_all('refseq')
389
+ assembly_status = bs_content.find_all('assemblystatus')
390
+ species_name = bs_content.find_all('speciesname')
391
+ temp_strains = bs_content.find_all('infraspecieslist')
392
+ assembly_name = [i.text for i in assembly_name]
393
+ genbank_ids = [i.text for i in genbank_ids]
394
+ refseq_ids = [i.text for i in refseq_ids]
395
+ assembly_status = [i.text for i in assembly_status]
396
+ species_name = [i.text for i in species_name]
397
+ ids = [int(i) for i in ids]
398
+ strains = []
399
+ for i in range(len(genbank_ids)):
400
+ temp_str = str(temp_strains[i])
401
+ #temp_str = BeautifulSoup(temp_str, 'lxml')
402
+ temp_str = BeautifulSoup(temp_str, 'html.parser')
403
+ temp_str = temp_str.find('sub_value')
404
+ if temp_str != None:
405
+ strains.append(temp_str.text)
406
+ else:
407
+ strains.append('N/A')
408
+
409
+ self.loading_window.loading_bar.setValue(65)
410
+ QtCore.QCoreApplication.processEvents()
411
+
412
+ #Get ftp links
413
+ genbank_links = bs_content.find_all('ftppath_genbank')
414
+ refseq_links = bs_content.find_all('ftppath_refseq')
415
+ refseq_links = bs_content.find_all('ftppath_refseq')
416
+ genbank_links = [i.text for i in genbank_links]
417
+ refseq_links = [i.text for i in refseq_links]
418
+ self.genbank_ftp_dict = {}
419
+ self.refseq_ftp_dict = {}
420
+ for i in range(len(ids)):
421
+ if genbank_ids[i] == '':
422
+ self.genbank_ftp_dict[ids[i]] = ''
423
+ else:
424
+ self.genbank_ftp_dict[ids[i]] = genbank_links[i] + '/'
425
+ if refseq_ids[i] == '':
426
+ self.refseq_ftp_dict[ids[i]] = ''
427
+ else:
428
+ self.refseq_ftp_dict[ids[i]] = refseq_links[i] + '/'
429
+
430
+ self.loading_window.loading_bar.setValue(80)
431
+ QtCore.QCoreApplication.processEvents()
432
+
433
+ #Build dataframe
434
+ self.df = pd.DataFrame({'ID': ids,
435
+ 'Species Name' : species_name,
436
+ 'Strain' : strains,
437
+ 'Assembly Name' : assembly_name,
438
+ 'GenBank assembly accession': genbank_ids,
439
+ 'RefSeq assembly accession': refseq_ids,
440
+ 'Assembly Status': assembly_status})
441
+
442
+ self.loading_window.loading_bar.setValue(90)
443
+ QtCore.QCoreApplication.processEvents()
444
+
445
+ #Build table view
446
+ self.df.replace('', 'N/A', inplace=True)
447
+ self.model = PandasModel(self.df)
448
+ self.proxy = CustomProxyModel(self)
449
+ self.proxy.setSourceModel(self.model)
450
+ self.ncbi_table.setModel(self.proxy)
451
+ self.ncbi_table.resizeColumnsToContents()
452
+ self.comboBox.addItems(["{0}".format(col) for col in self.model._df.columns])
453
+ self.activateWindow()
454
+
455
+ #close loading gif
456
+ self.loading_window.hide()
457
+ self.loading_window.loading_bar.setValue(0)
458
+ QtCore.QCoreApplication.processEvents()
459
+ except Exception as e:
460
+ show_error("Error in query_db() in ncbi tool.", e)
461
+
462
+ @QtCore.pyqtSlot(int)
463
+ def on_view_horizontalHeader_sectionClicked(self, logicalIndex):
464
+ try:
465
+ self.logicalIndex = logicalIndex
466
+ self.menuValues = QtWidgets.QMenu(self)
467
+ self.signalMapper = QtCore.QSignalMapper(self)
468
+ self.comboBox.blockSignals(True)
469
+ self.comboBox.setCurrentIndex(logicalIndex)
470
+ self.comboBox.blockSignals(True)
471
+ valuesUnique = self.model._df.iloc[:, logicalIndex].unique()
472
+ if logicalIndex == 0:
473
+ valuesUnique = ['Sort: 0-9', 'Sort: 9-0']
474
+ elif logicalIndex == 2:
475
+ valuesUnique = ['Exclude N/A', 'Only N/A', 'Sort: A-Z', 'Sort: Z-A']
476
+ elif logicalIndex == 3:
477
+ valuesUnique = ['Sort: A-Z', 'Sort: Z-A']
478
+ elif logicalIndex == 4 or logicalIndex == 5:
479
+ valuesUnique = ['Exclude N/A', 'Only N/A', 'Sort: A-Z', 'Sort: Z-A']
480
+
481
+ actionAll = QtWidgets.QAction("All", self)
482
+ actionAll.triggered.connect(self.on_actionAll_triggered)
483
+ self.menuValues.addAction(actionAll)
484
+ self.menuValues.addSeparator()
485
+ for actionNumber, actionName in enumerate(sorted(list(set(valuesUnique)))):
486
+ action = QtWidgets.QAction(actionName, self)
487
+ self.signalMapper.setMapping(action, actionNumber)
488
+ action.triggered.connect(self.signalMapper.map)
489
+ self.menuValues.addAction(action)
490
+ self.signalMapper.mapped.connect(self.on_signalMapper_mapped)
491
+ headerPos = self.ncbi_table.mapToGlobal(self.horizontalHeader.pos())
492
+ posY = headerPos.y() + self.horizontalHeader.height()
493
+ posX = headerPos.x() + self.horizontalHeader.sectionViewportPosition(logicalIndex)
494
+
495
+ self.menuValues.exec_(QtCore.QPoint(posX, posY))
496
+ except Exception as e:
497
+ show_error("Error in on_view_horizontalHeader_sectionClicked() in ncbi tool.", e)
498
+
499
+ @QtCore.pyqtSlot()
500
+ def on_actionAll_triggered(self):
501
+ try:
502
+ filterColumn = self.logicalIndex
503
+ self.proxy.setFilter("", filterColumn)
504
+ except Exception as e:
505
+ show_error("Error in on_actionAll_triggered() in ncbi tool.", e)
506
+
507
+ @QtCore.pyqtSlot(int)
508
+ def on_signalMapper_mapped(self, i):
509
+ try:
510
+ indices = self.ncbi_table.selectionModel().selectedRows()
511
+ #stringAction = self.signalMapper.mapping(i).text()
512
+ if self.logicalIndex == 0:
513
+ if i == 0:
514
+ self.model.sort(self.logicalIndex, QtCore.Qt.DescendingOrder)
515
+ else:
516
+ self.model.sort(self.logicalIndex, QtCore.Qt.AscendingOrder)
517
+ elif self.logicalIndex == 3:
518
+ if i == 0:
519
+ self.model.sort(self.logicalIndex, QtCore.Qt.DescendingOrder)
520
+ else:
521
+ self.model.sort(self.logicalIndex, QtCore.Qt.AscendingOrder)
522
+ elif self.logicalIndex == 2 or self.logicalIndex == 4 or self.logicalIndex == 5:
523
+ if i == 0:
524
+ stringAction = "(?!^N/A$)(^.*$)"
525
+ filterColumn = self.logicalIndex
526
+ self.proxy.setFilter(stringAction, filterColumn)
527
+ elif i == 1:
528
+ stringAction = "N/A"
529
+ filterColumn = self.logicalIndex
530
+ self.proxy.setFilter(stringAction, filterColumn)
531
+ elif i == 2:
532
+ self.model.sort(self.logicalIndex, QtCore.Qt.DescendingOrder)
533
+ else:
534
+ self.model.sort(self.logicalIndex, QtCore.Qt.AscendingOrder)
535
+ elif self.logicalIndex == 6:
536
+ stringAction = self.signalMapper.mapping(i).text()
537
+ filterColumn = self.logicalIndex
538
+ self.proxy.setFilter(stringAction, filterColumn)
539
+ except Exception as e:
540
+ show_error("Error in on_signalMapper_mapped() in ncbi tool.", e)
541
+
542
+ @QtCore.pyqtSlot()
543
+ def download_files_wrapper(self):
544
+ try:
545
+ self.progressBar.setValue(0)
546
+
547
+ #make sure rows are present in table
548
+ if self.df.shape[0] == 0:
549
+ show_message(
550
+ fontSize=self.fontSize,
551
+ icon=QtWidgets.QMessageBox.Icon.Critical,
552
+ title="No Query Results",
553
+ message="Please run an NCBI query to fill the table with results to choose from!"
554
+ )
555
+ return
556
+
557
+ #make sure user has selected at least one row
558
+ indices = self.ncbi_table.selectionModel().selectedRows()
559
+ if len(indices) == 0:
560
+ show_message(
561
+ fontSize=self.fontSize,
562
+ icon=QtWidgets.QMessageBox.Icon.Critical,
563
+ title="No Rows Selected",
564
+ message="Please select rows from the table!"
565
+ )
566
+ return
567
+
568
+ threadCount = QtCore.QThreadPool.globalInstance().maxThreadCount() # Get thread count
569
+ if len(indices) > threadCount:
570
+ show_message(
571
+ fontSize=self.fontSize,
572
+ icon=QtWidgets.QMessageBox.Icon.Critical,
573
+ title="Too Many Selections!",
574
+ message="You only have " + str(threadCount) + " threads avaiable to download with.\n\nPlease select " + str(threadCount) + " or fewer rows."
575
+ )
576
+ return
577
+
578
+ #make sure file type is selected
579
+ if self.gbff_checkbox.isChecked() == False and self.fna_checkbox.isChecked() == False:
580
+ show_message(
581
+ fontSize=self.fontSize,
582
+ icon=QtWidgets.QMessageBox.Icon.Critical,
583
+ title="No File Type Selected",
584
+ message="No file type selected. Please select the file types you want to download!"
585
+ )
586
+ return
587
+ self.download_files()
588
+ except Exception as e:
589
+ show_error("Error in download_files_wrapper() in ncbi tool.", e)
590
+
591
+ ### When a new thread is started, spawn a label and progress bar for that thread and add it to the form layout.
592
+ def on_thread_start(self, data):
593
+ id = data # This is the id for the thread
594
+ tmp_lbl = QtWidgets.QLabel()
595
+ tmp_lbl.setText("Download(s) Started...")
596
+ tmp_bar = QtWidgets.QProgressBar()
597
+ self.labels[id] = tmp_lbl # Append thread label to list
598
+ self.progressbars[id] = tmp_bar # Append thread label to list
599
+ self.formLayout.addRow(tmp_lbl,tmp_bar) # Add label and bar to form layout
600
+
601
+ ### When a thread is finished, update its label and progressbar for the last time
602
+ def on_thread_finish(self,data):
603
+ id = data[0]
604
+ my_bool = data[1]
605
+ if my_bool: # If thread finished succesfully
606
+ self.progressbars[id].setValue(int(self.progressbars[id].maximum())) #Make sure progress bar is full
607
+ self.labels[id].setText("Download(s) Complete!") #Make sure progress bar is full
608
+ self.progressBar.setValue(int(self.progressBar.value()+1)) # Increment overall progress bar when a thread finishes
609
+ QtWidgets.QApplication.processEvents() # Allow the progress bar to update
610
+ else:
611
+ self.progressBar.setMaximum(int(self.progressBar.maximum()-1)) # Subtract 1 from progress bar to reflect failed thread.
612
+ show_message(
613
+ fontSize=self.fontSize,
614
+ icon=QtWidgets.QMessageBox.Icon.Critical,
615
+ title="Link Failed!",
616
+ message="Failed to find a valid link for ID: " + str(id) + ". Please make sure this ID is available in the selected database."
617
+ )
618
+ return
619
+
620
+ ### When a file download starts, update the label and progress bar
621
+ def on_file_start(self, data):
622
+ id = data[0] # This is the id for the thread
623
+ prompt = data[1] # This is the prompt to set the label to
624
+ maxval = int(data[2]) # This is the file size
625
+ self.progressbars[id].setValue(0) # Set max value of progressbar
626
+ self.progressbars[id].setMaximum(int(maxval/1e3)) # Set max value of progressbar
627
+
628
+ self.labels[id].setText(str(prompt)) # Set label text
629
+
630
+ ## Taken from StackOverflow: https://stackoverflow.com/questions/57607072/update-pyqt-progress-from-another-thread-running-ftp-download
631
+ def on_progress_ready(self, data):
632
+ id = data[0] # This is the id for the thread
633
+ val = int(data[1]) # This is the increment
634
+ self.progressbars[id].setValue(int(self.progressbars[id].value() + val/1e3)) # Increment progress bar
635
+
636
+ ### When a file download finishes, update the label and progress bar and add file name to list
637
+ def on_file_finish(self,data):
638
+ id = data[0] # This is the id for the thread
639
+ prompt = data[1] # This is the prompt to set the label to
640
+ file_name = data[2] # This is the filename
641
+ self.progressbars[id].setValue(int(self.progressbars[id].maximum())) # Make sure progress bar is set to max after finishing download
642
+ self.labels[id].setText(str(prompt))
643
+ self.files.append(str(file_name)) # Add filename to list of downloaded files
644
+
645
+ def clean_bars(self):
646
+ for key in self.progressbars:
647
+ label = self.formLayout.labelForField(self.progressbars[key])
648
+ if label is not None:
649
+ label.deleteLater()
650
+ self.progressbars[key].deleteLater()
651
+
652
+ self.labels.clear()
653
+ self.progressbars.clear()
654
+ self.threads.clear()
655
+ def clear_layout(self):
656
+ for i in reversed(range(self.formLayout.count())):
657
+ self.formLayout.itemAt(i).widget().deleteLater()
658
+
659
+ ## Taken from StackOverflow: https://stackoverflow.com/questions/57607072/update-pyqt-progress-from-another-thread-running-ftp-download
660
+ def download_files(self):
661
+ try:
662
+ self.labels = {} # Key is the ID, value is the QLabel
663
+ self.progressbars = {} # Key is the ID, value is the QProgressBar
664
+ self.threads = {} # Key is the ID, value is the QThread
665
+ indices = self.ncbi_table.selectionModel().selectedRows()
666
+ len_ind = len(indices) # Get number of rows
667
+ self.progressBar.setMaximum(int(len_ind)) # Set overall progress bar to be equal to number of rows being downloaded
668
+ if len_ind == 0:
669
+ return
670
+ if self.genbank_checkbox.isChecked() == False and self.refseq_checkbox.isChecked() == False:
671
+ return
672
+ self.progressLabel.setText("Download(s) Started...") # Update progresslabel
673
+ self.files = [] # Initialize list to hold downloaded files
674
+ for index in indices: # For selected row(s) in table
675
+ NewIndex = self.ncbi_table.model().index(index.row(), 0) # Get index of selected row
676
+ id = self.ncbi_table.model().data(NewIndex) # Get ID from selected row
677
+ dirs = [] # Initialize list to hold links to ftp directories
678
+ if self.genbank_checkbox.isChecked():
679
+ genbank_ftp = self.genbank_ftp_dict[int(id)]
680
+ dirs.append(genbank_ftp)
681
+ else:
682
+ refseq_ftp = self.refseq_ftp_dict[int(id)]
683
+ dirs.append(refseq_ftp)
684
+ for dir in dirs: # Loop through the ftp links
685
+ url = str(dir).replace('ftp://ftp.ncbi.nlm.nih.gov', '') # Create new link
686
+ downloader = DownloadThread(parent=self,url=url,id=id) # Create a thread for the download
687
+ downloader.started.connect(self.on_thread_start) # Connect signal to function
688
+ downloader.finished.connect(self.on_thread_finish) # Connect signal to function
689
+ downloader.file_started.connect(self.on_file_start)
690
+ downloader.file_finished.connect(self.on_file_finish)
691
+ downloader.data_progress.connect(self.on_progress_ready) # Connect signal to function
692
+ self.threads[url] = downloader # Add thread to dictionary using the url as a key
693
+ downloader.start()
694
+ """ Make sure all threads are done before checking to see if files were downloaded """
695
+ while len([self.threads[t] for t in self.threads if self.threads[t].isRunning()]) > 0:
696
+ time.sleep(0.1)
697
+ QtWidgets.QApplication.processEvents()
698
+ self.progressBar.setValue(int(self.progressBar.maximum())) # Set progressBar to maximum
699
+ self.progressLabel.setText("Download(s) Complete!") # Set progressBar to maximum
700
+
701
+ self.clean_bars() # Clear out all the bars now that downloading is done
702
+
703
+ for i in range(len(self.files)):
704
+ self.files[i] = self.files[i].replace('.gz', '')
705
+ if platform.system() == 'Windows':
706
+ self.files[i] = self.files[i][self.files[i].rfind("\\")+1:]
707
+ else:
708
+ self.files[i] = self.files[i][self.files[i].rfind("/") + 1:]
709
+
710
+ if len(self.files) > 0:
711
+ self.rename_files(self.files)
712
+ else:
713
+ self.clear_layout()
714
+ self.clean_bars() # Clear out all the bars since the download failed
715
+ show_message(
716
+ fontSize = self.fontSize,
717
+ icon = QtWidgets.QMessageBox.Icon.Critical,
718
+ title = "No Files Downloaded",
719
+ message = "No files were downloaded from the selected NCBI files. Please make sure the selected files are available in the database selected."
720
+ )
721
+ return
722
+
723
+ except Exception as e:
724
+ show_error("Error in download_files() in ncbi tool.", e)
725
+
726
+ @QtCore.pyqtSlot()
727
+ def select_all(self):
728
+ try:
729
+ if self.all_rows.isChecked():
730
+ self.ncbi_table.selectAll()
731
+ else:
732
+ self.ncbi_table.clearSelection()
733
+ except Exception as e:
734
+ show_error("Error in select_all() in ncbi tool.", e)
735
+
736
+ def rename_files(self, files):
737
+ try:
738
+ msgBox = QtWidgets.QMessageBox()
739
+ msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
740
+ msgBox.setIcon(QtWidgets.QMessageBox.Icon.Question)
741
+ msgBox.setWindowTitle("Rename Files")
742
+ msgBox.setText("Would you like to rename the downloaded files?")
743
+ msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Yes)
744
+ msgBox.addButton(QtWidgets.QMessageBox.StandardButton.No)
745
+ msgBox.exec()
746
+
747
+ if msgBox.result() == QtWidgets.QMessageBox.Yes:
748
+ self.rename_window.rename_table.setRowCount(len(files))
749
+ cnt = 0
750
+ for file in files:
751
+ item = QtWidgets.QTableWidgetItem(file)
752
+ item.setFlags(QtCore.Qt.ItemIsEnabled)
753
+ self.rename_window.rename_table.setItem(cnt, 0, item)
754
+
755
+ self.rename_window.rename_table.setCellWidget(cnt, 1, QtWidgets.QLineEdit())
756
+ cnt += 1
757
+
758
+ header = self.rename_window.rename_table.horizontalHeader()
759
+ self.rename_window.rename_table.resizeColumnsToContents()
760
+ header.setSectionResizeMode(0, QtWidgets.QHeaderView.ResizeToContents)
761
+ header.setSectionResizeMode(1, QtWidgets.QHeaderView.Stretch)
762
+ center_ui(self.rename_window)
763
+ self.rename_window.show()
764
+ self.rename_window.activateWindow()
765
+ else:
766
+ GlobalSettings.mainWindow.fill_annotation_dropdown()
767
+ self.goToPrompt.show()
768
+ self.goToPrompt.activateWindow()
769
+ except Exception as e:
770
+ show_error("Error in rename_files() in ncbi tool.", e)
771
+
772
+ def rename_go_back(self):
773
+ try:
774
+ self.rename_window.close()
775
+ except Exception as e:
776
+ show_error("Error in rename_go_back() in ncbi tool.", e)
777
+
778
+ def submit_rename(self):
779
+ try:
780
+ #loop through columns and rename the files
781
+ for row in range(self.rename_window.rename_table.rowCount()):
782
+ orig = str(self.rename_window.rename_table.item(row, 0).text())
783
+ new = str(self.rename_window.rename_table.cellWidget(row, 1).text())
784
+ if new != "":
785
+ if orig.find(".gbff")!= -1:
786
+ if new.find(".") != -1:
787
+ new = new[:new.find(".")]
788
+ new = new + ".gbff"
789
+ elif orig.find(".fna") != -1:
790
+ if new.find(".") != -1:
791
+ new = new[:new.find(".")]
792
+ new = new + ".fna"
793
+
794
+ if platform.system() == "Windows":
795
+ if new.find(".gbff") != -1:
796
+ if os.path.isfile(GlobalSettings.CSPR_DB + "\\GBFF\\" + new) == False:
797
+ os.rename(GlobalSettings.CSPR_DB + "\\GBFF\\" + orig, GlobalSettings.CSPR_DB + "\\GBFF\\" + new)
798
+ else:
799
+ show_message(
800
+ fontSize = self.fontSize,
801
+ icon = QtWidgets.QMessageBox.Icon.Critical,
802
+ title = "Renaming Error",
803
+ message = "The filename: " + str(new) + " already exists. Please use a different name."
804
+ )
805
+ return
806
+ else:
807
+ if os.path.isfile(GlobalSettings.CSPR_DB + "\\FNA\\" + new) == False:
808
+ os.rename(GlobalSettings.CSPR_DB + "\\FNA\\" + orig, GlobalSettings.CSPR_DB + "\\FNA\\" + new)
809
+ else:
810
+ show_message(
811
+ fontSize = self.fontSize,
812
+ icon = QtWidgets.QMessageBox.Icon.Critical,
813
+ title = "Renaming Error",
814
+ message = "The filename: " + str(new) + " already exists. Please use a different name."
815
+ )
816
+ return
817
+ else:
818
+ #unix cannot have spaces in paths
819
+ new = new.replace(" ","")
820
+ if new.find(".gbff") != -1:
821
+ if os.path.isfile(GlobalSettings.CSPR_DB + "/GBFF/" + new) == False:
822
+ os.rename(GlobalSettings.CSPR_DB + "/GBFF/" + orig, GlobalSettings.CSPR_DB + "/GBFF/" + new)
823
+ else:
824
+ show_message(
825
+ fontSize = self.fontSize,
826
+ icon = QtWidgets.QMessageBox.Icon.Critical,
827
+ title = "Renaming Error",
828
+ message = "The filename: " + str(new) + " already exists. Please use a different name."
829
+ )
830
+ return
831
+ else:
832
+ if os.path.isfile(GlobalSettings.CSPR_DB + "/FNA/" + new) == False:
833
+ os.rename(GlobalSettings.CSPR_DB + "/FNA/" + orig, GlobalSettings.CSPR_DB + "/FNA/" + new)
834
+ else:
835
+ show_message(
836
+ fontSize = self.fontSize,
837
+ icon = QtWidgets.QMessageBox.Icon.Critical,
838
+ title = "Renaming Error",
839
+ message = "The filename: " + str(new) + " already exists. Please use a different name."
840
+ )
841
+ return
842
+
843
+ self.rename_window.rename_table.setRowCount(0)
844
+ self.rename_window.close()
845
+ GlobalSettings.mainWindow.fill_annotation_dropdown()
846
+ center_ui(self.goToPrompt)
847
+ self.goToPrompt.show()
848
+ self.goToPrompt.activateWindow()
849
+ except Exception as e:
850
+ show_error("Error in submit_rename() in ncbi tool.", e)
851
+
852
+ def stay(self):
853
+ try:
854
+ self.goToPrompt.hide()
855
+ except Exception as e:
856
+ show_error("Error in stay() in ncbi tool.", e)
857
+
858
+ def close(self):
859
+ try:
860
+ self.hide()
861
+ self.goToPrompt.hide()
862
+ except Exception as e:
863
+ show_error("Error in close() in ncbi tool.", e)
864
+
865
+ class goToPrompt(QtWidgets.QMainWindow):
866
+ def __init__(self):
867
+ try:
868
+ super(goToPrompt, self).__init__()
869
+ uic.loadUi(GlobalSettings.appdir + 'ui/ncbi_nav_page.ui', self)
870
+ self.setWindowTitle("Navigate")
871
+ self.setWindowIcon(Qt.QIcon(GlobalSettings.appdir + "cas9image.ico"))
872
+ self.label.setText("Successfully downloaded file(s) to the CASPERdb directory:\n" + GlobalSettings.CSPR_DB)
873
+ self.label.setAlignment(QtCore.Qt.AlignCenter)
874
+ groupbox_style = """
875
+ QGroupBox:title{subcontrol-origin: margin;
876
+ left: 10px;
877
+ padding: 0 5px 0 5px;}
878
+ QGroupBox#groupBox{border: 2px solid rgb(111,181,110);
879
+ border-radius: 9px;
880
+ font: bold 14pt 'Arial';
881
+ margin-top: 10px;}"""
882
+ self.groupBox.setStyleSheet(groupbox_style)
883
+
884
+ # self.scaleUI()
885
+ scale_ui(self, custom_scale_width=575, custom_scale_height=225)
886
+ except Exception as e:
887
+ show_error("Error initializing goToPrompt class in ncbi tool.", e)
888
+
889
+ class rename_window(QtWidgets.QMainWindow):
890
+ def __init__(self):
891
+ try:
892
+ super(rename_window, self).__init__()
893
+ uic.loadUi(GlobalSettings.appdir + "ui/ncbi_rename_window.ui", self)
894
+ self.setWindowIcon(Qt.QIcon(GlobalSettings.appdir + "cas9image.ico"))
895
+ self.setWindowTitle("Rename Files")
896
+ self.rename_table.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents)
897
+ self.rename_table.setColumnCount(2)
898
+ self.rename_table.setHorizontalHeaderLabels(['Original Filename', 'New Filename'])
899
+
900
+ scale_ui(self, custom_scale_width=575, custom_scale_height=300)
901
+ except Exception as e:
902
+ show_error("Error initializing rename_window class in ncbi tool.", e)
903
+
904
+ #loading window UI class for when data is loading
905
+ class loading_window(QtWidgets.QMainWindow):
906
+ def __init__(self):
907
+ try:
908
+ super(loading_window, self).__init__()
909
+ uic.loadUi(GlobalSettings.appdir + "ui/loading_data_form.ui", self)
910
+ self.loading_bar.setValue(0)
911
+ self.setWindowTitle("Loading NCBI Data")
912
+ self.setWindowIcon(Qt.QIcon(GlobalSettings.appdir + "cas9image.ico"))
913
+ scale_ui(self, custom_scale_width=450, custom_scale_height=125)
914
+ except Exception as e:
915
+ show_error("Error initializing loading_window class in ncbi tool.", e)
populationAnalysis.py → controllers/populationAnalysis.py RENAMED
@@ -1,9 +1,9 @@
1
  from PyQt5 import QtWidgets, Qt, QtGui, QtCore, uic
2
  from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
3
  from matplotlib.figure import Figure
4
- import GlobalSettings
5
  import os
6
- import Algorithms
7
  import numpy as np
8
  from PyQt5.QtWidgets import *
9
  import gzip
@@ -12,21 +12,15 @@ import itertools
12
  import matplotlib.patches as patches
13
  import mplcursors
14
  import copy
15
- import traceback
16
- import math
17
 
18
- #global logger
19
  logger = GlobalSettings.logger
20
 
21
-
22
- #population analysis class
23
  class Pop_Analysis(QtWidgets.QMainWindow):
24
-
25
- #init class
26
  def __init__(self):
27
  try:
28
  super(Pop_Analysis, self).__init__()
29
- uic.loadUi(GlobalSettings.appdir + 'pop.ui', self)
30
  self.setWindowIcon(Qt.QIcon(GlobalSettings.appdir + "cas9image.ico"))
31
  self.goBackButton.clicked.connect(self.go_back)
32
  self.analyze_button.clicked.connect(self.pre_analyze)
@@ -45,7 +39,6 @@ class Pop_Analysis(QtWidgets.QMainWindow):
45
  self.colormap_layout.setContentsMargins(0,0,0,0)
46
  self.colormap_canvas = MplCanvas(self) ###Initialize new Canvas
47
 
48
-
49
  groupbox_style = """
50
  QGroupBox:title{subcontrol-origin: margin;
51
  left: 10px;
@@ -107,160 +100,34 @@ class Pop_Analysis(QtWidgets.QMainWindow):
107
  self.index_to_db = {}
108
  self.name_to_db = {}
109
  self.cspr_files = []
110
- self.db_files = []
111
- self.Endos = {}
112
  self.seeds = []
113
 
114
  self.loading_window = loading_window()
115
 
116
- #scale UI
117
  self.first_show = True
118
- self.scaleUI()
119
 
120
  except Exception as e:
121
- logger.critical("Error initializing population analysis.")
122
- logger.critical(e)
123
- logger.critical(traceback.format_exc())
124
- msgBox = QtWidgets.QMessageBox()
125
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
126
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
127
- msgBox.setWindowTitle("Fatal Error")
128
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
129
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
130
- msgBox.exec()
131
-
132
-
133
- exit(-1)
134
-
135
- #scale the UI based on current screen
136
- def scaleUI(self):
137
- try:
138
- self.repaint()
139
- QtWidgets.QApplication.processEvents()
140
-
141
- screen = QtWidgets.QApplication.desktop().screenNumber(QtWidgets.QApplication.desktop().cursor().pos())
142
- screen = QtWidgets.QApplication.screens()[screen]
143
- dpi = screen.physicalDotsPerInch()
144
- self.dpi = dpi
145
- width = screen.geometry().width()
146
- height = screen.geometry().height()
147
-
148
- # font scaling
149
- fontSize = 12
150
- self.fontSize = fontSize
151
- self.centralWidget().setStyleSheet("font: " + str(fontSize) + "pt 'Arial';")
152
- self.menuBar().setStyleSheet("font: " + str(fontSize) + "pt 'Arial';")
153
-
154
- # CASPER header scaling
155
- fontSize = 30
156
- self.title.setStyleSheet("font: bold " + str(fontSize) + "pt 'Arial';")
157
-
158
- self.groupBox.setMaximumWidth(int(width * 0.3))
159
-
160
- self.adjustSize()
161
-
162
- currentWidth = self.size().width()
163
- currentHeight = self.size().height()
164
-
165
- # window scaling
166
- # 1920x1080 => 1150x650
167
- scaledWidth = int((width * 1150) / 1920)
168
- scaledHeight = int((height * 650) / 1080)
169
-
170
- if scaledHeight < currentHeight:
171
- scaledHeight = currentHeight
172
- if scaledWidth < currentWidth:
173
- scaledWidth = currentWidth
174
-
175
- screen = QtWidgets.QApplication.desktop().screenNumber(QtWidgets.QApplication.desktop().cursor().pos())
176
- centerPoint = QtWidgets.QApplication.desktop().screenGeometry(screen).center()
177
- x = centerPoint.x()
178
- y = centerPoint.y()
179
- x = x - (math.ceil(scaledWidth / 2))
180
- y = y - (math.ceil(scaledHeight / 2))
181
- self.setGeometry(x, y, scaledWidth, scaledHeight)
182
- self.repaint()
183
- QtWidgets.QApplication.processEvents()
184
-
185
- except Exception as e:
186
- logger.critical("Error in scaleUI() in population analysis.")
187
- logger.critical(e)
188
- logger.critical(traceback.format_exc())
189
- msgBox = QtWidgets.QMessageBox()
190
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
191
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
192
- msgBox.setWindowTitle("Fatal Error")
193
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
194
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
195
- msgBox.exec()
196
-
197
-
198
- exit(-1)
199
-
200
- #center UI on current screen
201
- def centerUI(self):
202
- try:
203
- self.repaint()
204
- QtWidgets.QApplication.processEvents()
205
-
206
- #center window on current screen
207
- width = self.width()
208
- height = self.height()
209
- screen = QtWidgets.QApplication.desktop().screenNumber(QtWidgets.QApplication.desktop().cursor().pos())
210
- centerPoint = QtWidgets.QApplication.desktop().screenGeometry(screen).center()
211
- x = centerPoint.x()
212
- y = centerPoint.y()
213
- x = x - (math.ceil(width / 2))
214
- y = y - (math.ceil(height / 2))
215
- self.setGeometry(x, y, width, height)
216
-
217
- self.repaint()
218
- QtWidgets.QApplication.processEvents()
219
- except Exception as e:
220
- logger.critical("Error in centerUI() in population analysis.")
221
- logger.critical(e)
222
- logger.critical(traceback.format_exc())
223
- msgBox = QtWidgets.QMessageBox()
224
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
225
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
226
- msgBox.setWindowTitle("Fatal Error")
227
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
228
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
229
- msgBox.exec()
230
-
231
-
232
- exit(-1)
233
 
234
  #export shared seed table to csv function
235
  def export_tool(self):
236
  try:
237
  select_items = self.table2.selectedItems()
238
- if len(select_items) <= 0:
239
- msgBox = QtWidgets.QMessageBox()
240
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
241
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
242
- msgBox.setWindowTitle("Nothing Selected")
243
- msgBox.setText("No targets were highlighted. Please highlight the targets you want to be exported to a CSV File!")
244
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
245
- msgBox.exec()
246
 
 
 
 
 
 
 
 
247
  return
 
248
  GlobalSettings.mainWindow.export_tool_window.launch(select_items,"pa")
249
  except Exception as e:
250
- logger.critical("Error in export_tool() in population analysis.")
251
- logger.critical(e)
252
- logger.critical(traceback.format_exc())
253
- msgBox = QtWidgets.QMessageBox()
254
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
255
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
256
- msgBox.setWindowTitle("Fatal Error")
257
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
258
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
259
- msgBox.exec()
260
-
261
-
262
- exit(-1)
263
-
264
  # this function calls the popParser function and fills all the tables
265
  def pre_analyze(self):
266
  try:
@@ -272,16 +139,13 @@ class Pop_Analysis(QtWidgets.QMainWindow):
272
  #get selected indexes
273
  selected_indexes = self.org_Table.selectionModel().selectedRows()
274
 
275
- # error check
276
  if len(selected_indexes) == 0:
277
- msgBox = QtWidgets.QMessageBox()
278
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
279
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
280
- msgBox.setWindowTitle("Error")
281
- msgBox.setText("Please select CSPR file(s) for analysis.")
282
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
283
- msgBox.exec()
284
-
285
  return
286
 
287
  #get cspr and db filenames
@@ -293,58 +157,21 @@ class Pop_Analysis(QtWidgets.QMainWindow):
293
  self.get_org_names()
294
  self.fill_data()
295
  except Exception as e:
296
- logger.critical("Error in pre_analyze() in population analysis.")
297
- logger.critical(e)
298
- logger.critical(traceback.format_exc())
299
- msgBox = QtWidgets.QMessageBox()
300
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
301
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
302
- msgBox.setWindowTitle("Fatal Error")
303
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
304
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
305
- msgBox.exec()
306
-
307
-
308
- exit(-1)
309
-
310
- #wrapper for calling get_data()
311
  def launch(self):
312
  try:
313
  self.get_data()
314
  except Exception as e:
315
- logger.critical("Error in launch() in population analysis.")
316
- logger.critical(e)
317
- logger.critical(traceback.format_exc())
318
- msgBox = QtWidgets.QMessageBox()
319
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
320
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
321
- msgBox.setWindowTitle("Fatal Error")
322
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
323
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
324
- msgBox.exec()
325
-
326
-
327
- exit(-1)
328
-
329
  #responsible for calling all loading/analysis functions for loading the shared seeds table and generating the heatmap based on selected organisms
330
  def get_data(self):
331
  try:
332
  self.fillEndo()
333
  except Exception as e:
334
- logger.critical("Error in get_data() in population analysis.")
335
- logger.critical(e)
336
- logger.critical(traceback.format_exc())
337
- msgBox = QtWidgets.QMessageBox()
338
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
339
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
340
- msgBox.setWindowTitle("Fatal Error")
341
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
342
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
343
- msgBox.exec()
344
-
345
-
346
- exit(-1)
347
-
348
  # this function opens CASPERinfo and builds the dropdown menu of selectable endonucleases
349
  def fillEndo(self):
350
  try:
@@ -381,20 +208,8 @@ class Pop_Analysis(QtWidgets.QMainWindow):
381
  self.endoBox.currentIndexChanged.connect(self.change_endo)
382
  self.change_endo()
383
  except Exception as e:
384
- logger.critical("Error in fillEndo() in population analysis.")
385
- logger.critical(e)
386
- logger.critical(traceback.format_exc())
387
- msgBox = QtWidgets.QMessageBox()
388
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
389
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
390
- msgBox.setWindowTitle("Fatal Error")
391
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
392
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
393
- msgBox.exec()
394
-
395
-
396
- exit(-1)
397
-
398
  #event handler for updating the organism options based on the endo selected
399
  def change_endo(self):
400
  try:
@@ -437,26 +252,14 @@ class Pop_Analysis(QtWidgets.QMainWindow):
437
 
438
  self.org_Table.resizeColumnsToContents()
439
  except Exception as e:
440
- logger.critical("Error in change_endo() in population analysis.")
441
- logger.critical(e)
442
- logger.critical(traceback.format_exc())
443
- msgBox = QtWidgets.QMessageBox()
444
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
445
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
446
- msgBox.setWindowTitle("Fatal Error")
447
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
448
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
449
- msgBox.exec()
450
-
451
-
452
- exit(-1)
453
-
454
  #fills shared seed table with data from analysis
455
  def fill_data(self):
456
  try:
457
  #update progress bar
458
  self.loading_window.loading_bar.setValue(5)
459
- self.loading_window.centerUI()
460
  self.loading_window.show()
461
  QtCore.QCoreApplication.processEvents()
462
 
@@ -510,12 +313,14 @@ class Pop_Analysis(QtWidgets.QMainWindow):
510
  data = c.execute("SELECT count, three, five, pam, score, location FROM repeats WHERE seed = ? ",(seed,)).fetchone()
511
  if data != None:
512
  data = list(data)
 
513
  org_count += 1
514
  total_count += int(data[0])
515
  threes += data[1].split(",")
516
  fives += data[2].split(",")
517
  pams += data[3].split(",")
518
  scores += data[4].split(",")
 
519
  locs += data[5].split(",")
520
 
521
  self.counts.append(total_count)
@@ -629,19 +434,7 @@ class Pop_Analysis(QtWidgets.QMainWindow):
629
  self.loading_window.hide()
630
  QtCore.QCoreApplication.processEvents()
631
  except Exception as e:
632
- logger.critical("Error in fill_data() in population analysis.")
633
- logger.critical(e)
634
- logger.critical(traceback.format_exc())
635
- msgBox = QtWidgets.QMessageBox()
636
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
637
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
638
- msgBox.setWindowTitle("Fatal Error")
639
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
640
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
641
- msgBox.exec()
642
-
643
-
644
- exit(-1)
645
 
646
  #function to allow user to search for a specific seed amongst the organisms analyzed
647
  def custom_seed_search(self):
@@ -672,13 +465,12 @@ class Pop_Analysis(QtWidgets.QMainWindow):
672
 
673
  if len(self.seeds) == 0:
674
  self.loading_window.hide()
675
- msgBox = QtWidgets.QMessageBox()
676
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
677
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
678
- msgBox.setWindowTitle("Error")
679
- msgBox.setText("No analysis has been run to be able to search for a specific seed.")
680
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
681
- msgBox.exec()
682
  return
683
 
684
  increase_val = float(15 / len(self.seeds))
@@ -717,13 +509,12 @@ class Pop_Analysis(QtWidgets.QMainWindow):
717
 
718
  if none_data == True:
719
  self.loading_window.hide()
720
- msgBox = QtWidgets.QMessageBox()
721
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
722
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
723
- msgBox.setWindowTitle("Seed Error")
724
- msgBox.setText(seed + " : No such seed exists in the repeats section of any organism selected.")
725
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
726
- msgBox.exec()
727
  return
728
 
729
  else:
@@ -839,24 +630,11 @@ class Pop_Analysis(QtWidgets.QMainWindow):
839
  self.loading_window.hide()
840
  QtCore.QCoreApplication.processEvents()
841
  except Exception as e:
842
- logger.critical("Error in custom_seed_search() in population analysis.")
843
- logger.critical(e)
844
- logger.critical(traceback.format_exc())
845
- msgBox = QtWidgets.QMessageBox()
846
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
847
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
848
- msgBox.setWindowTitle("Fatal Error")
849
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
850
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
851
- msgBox.exec()
852
-
853
-
854
- exit(-1)
855
-
856
  #db_files is an array of database files for the organisms that will be looked at for shared seeds
857
  def get_shared_seeds(self, db_files, limit=False):
858
  try:
859
- #vars
860
  aliases = []
861
 
862
  #get db attachment aliases
@@ -912,20 +690,8 @@ class Pop_Analysis(QtWidgets.QMainWindow):
912
  return seeds
913
 
914
  except Exception as e:
915
- logger.critical("Error in get_shared_seeds() in population analysis.")
916
- logger.critical(e)
917
- logger.critical(traceback.format_exc())
918
- msgBox = QtWidgets.QMessageBox()
919
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
920
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
921
- msgBox.setWindowTitle("Fatal Error")
922
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
923
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
924
- msgBox.exec()
925
-
926
-
927
- exit(-1)
928
-
929
  #get the names of organism in current directory
930
  def get_org_names(self):
931
  try:
@@ -943,20 +709,8 @@ class Pop_Analysis(QtWidgets.QMainWindow):
943
  kstats = kstats.split(",")
944
  self.org_names[org_name] = len(kstats) - 1
945
  except Exception as e:
946
- logger.critical("Error in get_org_names() in population analysis.")
947
- logger.critical(e)
948
- logger.critical(traceback.format_exc())
949
- msgBox = QtWidgets.QMessageBox()
950
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
951
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
952
- msgBox.setWindowTitle("Fatal Error")
953
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
954
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
955
- msgBox.exec()
956
-
957
-
958
- exit(-1)
959
-
960
  #plot the heatmap graph
961
  def plot_3D_graph(self):
962
  try:
@@ -1028,34 +782,18 @@ class Pop_Analysis(QtWidgets.QMainWindow):
1028
 
1029
  self.colormap_canvas.draw()
1030
  except Exception as e:
1031
- logger.critical("Error in plot_3D_graph() in population analysis.")
1032
- logger.critical(e)
1033
- logger.critical(traceback.format_exc())
1034
- msgBox = QtWidgets.QMessageBox()
1035
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1036
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1037
- msgBox.setWindowTitle("Fatal Error")
1038
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1039
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1040
- msgBox.exec()
1041
-
1042
-
1043
- exit(-1)
1044
 
1045
  #find the locations of selected seeds to load into the location table
1046
  def find_locations(self):
1047
  try:
1048
- #error checking
1049
  if len(self.table2.selectedItems()) == 0:
1050
- msgBox = QtWidgets.QMessageBox()
1051
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1052
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1053
- msgBox.setWindowTitle("Error")
1054
- msgBox.setText("Please select at least 1 seed to find locations of.")
1055
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
1056
- msgBox.exec()
1057
-
1058
- self.loc_finder_table.setRowCount(0)
1059
  return
1060
 
1061
  #get seeds from selected rows in table
@@ -1111,19 +849,7 @@ class Pop_Analysis(QtWidgets.QMainWindow):
1111
 
1112
  self.loc_finder_table.resizeColumnsToContents()
1113
  except Exception as e:
1114
- logger.critical("Error in find_locations() in population analysis.")
1115
- logger.critical(e)
1116
- logger.critical(traceback.format_exc())
1117
- msgBox = QtWidgets.QMessageBox()
1118
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1119
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1120
- msgBox.setWindowTitle("Fatal Error")
1121
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1122
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1123
- msgBox.exec()
1124
-
1125
-
1126
- exit(-1)
1127
 
1128
  # this function clears the loc_finder_table
1129
  def clear_loc_table(self):
@@ -1131,20 +857,8 @@ class Pop_Analysis(QtWidgets.QMainWindow):
1131
  self.loc_finder_table.clearContents()
1132
  self.loc_finder_table.setRowCount(0)
1133
  except Exception as e:
1134
- logger.critical("Error in clear_loc_table() in population analysis.")
1135
- logger.critical(e)
1136
- logger.critical(traceback.format_exc())
1137
- msgBox = QtWidgets.QMessageBox()
1138
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1139
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1140
- msgBox.setWindowTitle("Fatal Error")
1141
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1142
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1143
- msgBox.exec()
1144
-
1145
-
1146
- exit(-1)
1147
-
1148
  # sorting function for table2 - shared seeds table: IE the table in top-right
1149
  def table2_sorting(self, logicalIndex):
1150
  try:
@@ -1154,19 +868,7 @@ class Pop_Analysis(QtWidgets.QMainWindow):
1154
  else:
1155
  self.table2.sortItems(logicalIndex, QtCore.Qt.AscendingOrder)
1156
  except Exception as e:
1157
- logger.critical("Error in table2_sorting() in population analysis.")
1158
- logger.critical(e)
1159
- logger.critical(traceback.format_exc())
1160
- msgBox = QtWidgets.QMessageBox()
1161
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1162
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1163
- msgBox.setWindowTitle("Fatal Error")
1164
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1165
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1166
- msgBox.exec()
1167
-
1168
-
1169
- exit(-1)
1170
 
1171
  # sorting for location table: IE table in bottom right
1172
  def loc_table_sorter(self, logicalIndex):
@@ -1177,58 +879,22 @@ class Pop_Analysis(QtWidgets.QMainWindow):
1177
  else:
1178
  self.loc_finder_table.sortItems(logicalIndex, QtCore.Qt.AscendingOrder)
1179
  except Exception as e:
1180
- logger.critical("Error in loc_table_sorter() in population analysis.")
1181
- logger.critical(e)
1182
- logger.critical(traceback.format_exc())
1183
- msgBox = QtWidgets.QMessageBox()
1184
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1185
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1186
- msgBox.setWindowTitle("Fatal Error")
1187
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1188
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1189
- msgBox.exec()
1190
-
1191
-
1192
- exit(-1)
1193
-
1194
  #clears the table showcasing shared seeds
1195
  def clear(self):
1196
  try:
1197
  self.table2.setRowCount(0)
1198
  except Exception as e:
1199
- logger.critical("Error in clear() in population analysis.")
1200
- logger.critical(e)
1201
- logger.critical(traceback.format_exc())
1202
- msgBox = QtWidgets.QMessageBox()
1203
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1204
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1205
- msgBox.setWindowTitle("Fatal Error")
1206
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1207
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1208
- msgBox.exec()
1209
-
1210
-
1211
- exit(-1)
1212
-
1213
  #return to main function
1214
  def go_back(self):
1215
  try:
1216
  GlobalSettings.mainWindow.show()
1217
  self.hide()
1218
  except Exception as e:
1219
- logger.critical("Error in go_back() in population analysis.")
1220
- logger.critical(e)
1221
- logger.critical(traceback.format_exc())
1222
- msgBox = QtWidgets.QMessageBox()
1223
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1224
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1225
- msgBox.setWindowTitle("Fatal Error")
1226
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1227
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1228
- msgBox.exec()
1229
-
1230
-
1231
- exit(-1)
1232
 
1233
  # this function calls the close window class. Allows the user to choose what files they want to keep/delete
1234
  def closeEvent(self, event):
@@ -1236,134 +902,20 @@ class Pop_Analysis(QtWidgets.QMainWindow):
1236
  GlobalSettings.mainWindow.closeFunction()
1237
  event.accept()
1238
  except Exception as e:
1239
- logger.critical("Error in closeEvent() in population analysis.")
1240
- logger.critical(e)
1241
- logger.critical(traceback.format_exc())
1242
- msgBox = QtWidgets.QMessageBox()
1243
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1244
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1245
- msgBox.setWindowTitle("Fatal Error")
1246
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1247
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1248
- msgBox.exec()
1249
-
1250
-
1251
- exit(-1)
1252
-
1253
-
1254
  #loading window UI class for when data is loading
1255
  class loading_window(QtWidgets.QMainWindow):
1256
  def __init__(self):
1257
  try:
1258
  super(loading_window, self).__init__()
1259
- uic.loadUi(GlobalSettings.appdir + "loading_data_form.ui", self)
1260
  self.loading_bar.setValue(0)
1261
  self.setWindowTitle("Loading Data")
1262
  self.setWindowIcon(Qt.QIcon(GlobalSettings.appdir + "cas9image.ico"))
1263
- self.scaleUI()
1264
  except Exception as e:
1265
- logger.critical("Error initializing loading_window class in population analysis.")
1266
- logger.critical(e)
1267
- logger.critical(traceback.format_exc())
1268
- msgBox = QtWidgets.QMessageBox()
1269
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1270
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1271
- msgBox.setWindowTitle("Fatal Error")
1272
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1273
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1274
- msgBox.exec()
1275
-
1276
-
1277
- exit(-1)
1278
-
1279
- #scale UI based on current screen
1280
- def scaleUI(self):
1281
- try:
1282
- self.repaint()
1283
- QtWidgets.QApplication.processEvents()
1284
-
1285
- screen = self.screen()
1286
- dpi = screen.physicalDotsPerInch()
1287
- width = screen.geometry().width()
1288
- height = screen.geometry().height()
1289
-
1290
- # font scaling
1291
- fontSize = 12
1292
- self.centralWidget().setStyleSheet("font: " + str(fontSize) + "pt 'Arial';")
1293
-
1294
- self.adjustSize()
1295
-
1296
- currentWidth = self.size().width()
1297
- currentHeight = self.size().height()
1298
-
1299
- # scale/center window
1300
- scaledWidth = int((width * 450) / 1920)
1301
- scaledHeight = int((height * 125) / 1080)
1302
-
1303
- if scaledHeight < currentHeight:
1304
- scaledHeight = currentHeight
1305
- if scaledWidth < currentWidth:
1306
- scaledWidth = currentWidth
1307
-
1308
- screen = QtWidgets.QApplication.desktop().screenNumber(QtWidgets.QApplication.desktop().cursor().pos())
1309
- centerPoint = QtWidgets.QApplication.desktop().screenGeometry(screen).center()
1310
- x = centerPoint.x()
1311
- y = centerPoint.y()
1312
- x = x - (math.ceil(scaledWidth / 2))
1313
- y = y - (math.ceil(scaledHeight / 2))
1314
- self.setGeometry(x, y, scaledWidth, scaledHeight)
1315
-
1316
- self.repaint()
1317
- QtWidgets.QApplication.processEvents()
1318
- except Exception as e:
1319
- logger.critical("Error in scaleUI() in loading_window() class in population analysis.")
1320
- logger.critical(e)
1321
- logger.critical(traceback.format_exc())
1322
- msgBox = QtWidgets.QMessageBox()
1323
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1324
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1325
- msgBox.setWindowTitle("Fatal Error")
1326
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1327
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1328
- msgBox.exec()
1329
-
1330
-
1331
- exit(-1)
1332
-
1333
- #center UI on current screen
1334
- def centerUI(self):
1335
- try:
1336
- self.repaint()
1337
- QtWidgets.QApplication.processEvents()
1338
-
1339
- width = self.width()
1340
- height = self.height()
1341
- #scale/center window
1342
- screen = QtWidgets.QApplication.desktop().screenNumber(QtWidgets.QApplication.desktop().cursor().pos())
1343
- centerPoint = QtWidgets.QApplication.desktop().screenGeometry(screen).center()
1344
- x = centerPoint.x()
1345
- y = centerPoint.y()
1346
- x = x - (math.ceil(width / 2))
1347
- y = y - (math.ceil(height / 2))
1348
- self.setGeometry(x, y, width, height)
1349
-
1350
- self.repaint()
1351
- QtWidgets.QApplication.processEvents()
1352
- except Exception as e:
1353
- logger.critical("Error in centerUI() in loading_window() class in population analysis.")
1354
- logger.critical(e)
1355
- logger.critical(traceback.format_exc())
1356
- msgBox = QtWidgets.QMessageBox()
1357
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1358
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1359
- msgBox.setWindowTitle("Fatal Error")
1360
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1361
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1362
- msgBox.exec()
1363
-
1364
-
1365
- exit(-1)
1366
-
1367
 
1368
  #matplotlib canvas class for the heatmap graph
1369
  class MplCanvas(FigureCanvasQTAgg):
@@ -1374,16 +926,4 @@ class MplCanvas(FigureCanvasQTAgg):
1374
  self.axes.clear()
1375
  super(MplCanvas, self).__init__(fig)
1376
  except Exception as e:
1377
- logger.critical("Error initializing MplCanvas class in population analysis.")
1378
- logger.critical(e)
1379
- logger.critical(traceback.format_exc())
1380
- msgBox = QtWidgets.QMessageBox()
1381
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1382
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1383
- msgBox.setWindowTitle("Fatal Error")
1384
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1385
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1386
- msgBox.exec()
1387
-
1388
-
1389
- exit(-1)
 
1
  from PyQt5 import QtWidgets, Qt, QtGui, QtCore, uic
2
  from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
3
  from matplotlib.figure import Figure
4
+ import models.GlobalSettings as GlobalSettings
5
  import os
6
+ import utils.Algorithms as Algorithms
7
  import numpy as np
8
  from PyQt5.QtWidgets import *
9
  import gzip
 
12
  import matplotlib.patches as patches
13
  import mplcursors
14
  import copy
15
+ from utils.ui import show_error, scale_ui, center_ui, show_message
 
16
 
 
17
  logger = GlobalSettings.logger
18
 
 
 
19
  class Pop_Analysis(QtWidgets.QMainWindow):
 
 
20
  def __init__(self):
21
  try:
22
  super(Pop_Analysis, self).__init__()
23
+ uic.loadUi(GlobalSettings.appdir + 'ui/pop.ui', self)
24
  self.setWindowIcon(Qt.QIcon(GlobalSettings.appdir + "cas9image.ico"))
25
  self.goBackButton.clicked.connect(self.go_back)
26
  self.analyze_button.clicked.connect(self.pre_analyze)
 
39
  self.colormap_layout.setContentsMargins(0,0,0,0)
40
  self.colormap_canvas = MplCanvas(self) ###Initialize new Canvas
41
 
 
42
  groupbox_style = """
43
  QGroupBox:title{subcontrol-origin: margin;
44
  left: 10px;
 
100
  self.index_to_db = {}
101
  self.name_to_db = {}
102
  self.cspr_files = []
 
 
103
  self.seeds = []
104
 
105
  self.loading_window = loading_window()
106
 
 
107
  self.first_show = True
108
+ scale_ui(self, base_width=1920, base_height=1080, font_size=12, header_font_size=30)
109
 
110
  except Exception as e:
111
+ show_error("Error initializing population analysis.", e)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
 
113
  #export shared seed table to csv function
114
  def export_tool(self):
115
  try:
116
  select_items = self.table2.selectedItems()
 
 
 
 
 
 
 
 
117
 
118
+ if len(select_items) <= 0:
119
+ show_message(
120
+ fontSize=12,
121
+ icon=QtWidgets.QMessageBox.Icon.Critical,
122
+ title="Nothing Selected",
123
+ message="No targets were highlighted. Please highlight the targets you want to be exported to a CSV File!"
124
+ )
125
  return
126
+
127
  GlobalSettings.mainWindow.export_tool_window.launch(select_items,"pa")
128
  except Exception as e:
129
+ show_error("Error in export_tool() in population analysis.", e)
130
+
 
 
 
 
 
 
 
 
 
 
 
 
131
  # this function calls the popParser function and fills all the tables
132
  def pre_analyze(self):
133
  try:
 
139
  #get selected indexes
140
  selected_indexes = self.org_Table.selectionModel().selectedRows()
141
 
 
142
  if len(selected_indexes) == 0:
143
+ show_message(
144
+ fontSize=12,
145
+ icon=QtWidgets.QMessageBox.Icon.Critical,
146
+ title="Error",
147
+ message="Please select CSPR file(s) for analysis."
148
+ )
 
 
149
  return
150
 
151
  #get cspr and db filenames
 
157
  self.get_org_names()
158
  self.fill_data()
159
  except Exception as e:
160
+ show_error("Error in pre_analyze() in population analysis.", e)
161
+
 
 
 
 
 
 
 
 
 
 
 
 
 
162
  def launch(self):
163
  try:
164
  self.get_data()
165
  except Exception as e:
166
+ show_error("Error in launch() in population analysis.", e)
167
+
 
 
 
 
 
 
 
 
 
 
 
 
168
  #responsible for calling all loading/analysis functions for loading the shared seeds table and generating the heatmap based on selected organisms
169
  def get_data(self):
170
  try:
171
  self.fillEndo()
172
  except Exception as e:
173
+ show_error("Error in get_data() in population analysis.", e)
174
+
 
 
 
 
 
 
 
 
 
 
 
 
175
  # this function opens CASPERinfo and builds the dropdown menu of selectable endonucleases
176
  def fillEndo(self):
177
  try:
 
208
  self.endoBox.currentIndexChanged.connect(self.change_endo)
209
  self.change_endo()
210
  except Exception as e:
211
+ show_error("Error in fillEndo() in population analysis.", e)
212
+
 
 
 
 
 
 
 
 
 
 
 
 
213
  #event handler for updating the organism options based on the endo selected
214
  def change_endo(self):
215
  try:
 
252
 
253
  self.org_Table.resizeColumnsToContents()
254
  except Exception as e:
255
+ show_error("Error in change_endo() in population analysis.", e)
256
+
 
 
 
 
 
 
 
 
 
 
 
 
257
  #fills shared seed table with data from analysis
258
  def fill_data(self):
259
  try:
260
  #update progress bar
261
  self.loading_window.loading_bar.setValue(5)
262
+ center_ui(self.loading_window)
263
  self.loading_window.show()
264
  QtCore.QCoreApplication.processEvents()
265
 
 
313
  data = c.execute("SELECT count, three, five, pam, score, location FROM repeats WHERE seed = ? ",(seed,)).fetchone()
314
  if data != None:
315
  data = list(data)
316
+ print(data)
317
  org_count += 1
318
  total_count += int(data[0])
319
  threes += data[1].split(",")
320
  fives += data[2].split(",")
321
  pams += data[3].split(",")
322
  scores += data[4].split(",")
323
+ print(scores)
324
  locs += data[5].split(",")
325
 
326
  self.counts.append(total_count)
 
434
  self.loading_window.hide()
435
  QtCore.QCoreApplication.processEvents()
436
  except Exception as e:
437
+ show_error("Error in fill_data() in population analysis.", e)
 
 
 
 
 
 
 
 
 
 
 
 
438
 
439
  #function to allow user to search for a specific seed amongst the organisms analyzed
440
  def custom_seed_search(self):
 
465
 
466
  if len(self.seeds) == 0:
467
  self.loading_window.hide()
468
+ show_message(
469
+ fontSize=12,
470
+ icon=QtWidgets.QMessageBox.Icon.Critical,
471
+ title="Error",
472
+ message="No analysis has been run to be able to search for a specific seed."
473
+ )
 
474
  return
475
 
476
  increase_val = float(15 / len(self.seeds))
 
509
 
510
  if none_data == True:
511
  self.loading_window.hide()
512
+ show_message(
513
+ fontSize=12,
514
+ icon=QtWidgets.QMessageBox.Icon.Critical,
515
+ title="Seed Error",
516
+ message=seed + " : No such seed exists in the repeats section of any organism selected."
517
+ )
 
518
  return
519
 
520
  else:
 
630
  self.loading_window.hide()
631
  QtCore.QCoreApplication.processEvents()
632
  except Exception as e:
633
+ show_error("Error in custom_seed_search() in population analysis.", e)
634
+
 
 
 
 
 
 
 
 
 
 
 
 
635
  #db_files is an array of database files for the organisms that will be looked at for shared seeds
636
  def get_shared_seeds(self, db_files, limit=False):
637
  try:
 
638
  aliases = []
639
 
640
  #get db attachment aliases
 
690
  return seeds
691
 
692
  except Exception as e:
693
+ show_error("Error in get_shared_seeds() in population analysis.", e)
694
+
 
 
 
 
 
 
 
 
 
 
 
 
695
  #get the names of organism in current directory
696
  def get_org_names(self):
697
  try:
 
709
  kstats = kstats.split(",")
710
  self.org_names[org_name] = len(kstats) - 1
711
  except Exception as e:
712
+ show_error("Error in get_org_names() in population analysis.", e)
713
+
 
 
 
 
 
 
 
 
 
 
 
 
714
  #plot the heatmap graph
715
  def plot_3D_graph(self):
716
  try:
 
782
 
783
  self.colormap_canvas.draw()
784
  except Exception as e:
785
+ show_error("Error in plot_3D_graph() in population analysis.", e)
 
 
 
 
 
 
 
 
 
 
 
 
786
 
787
  #find the locations of selected seeds to load into the location table
788
  def find_locations(self):
789
  try:
 
790
  if len(self.table2.selectedItems()) == 0:
791
+ show_message(
792
+ fontSize=12,
793
+ icon=QtWidgets.QMessageBox.Icon.Critical,
794
+ title="Error",
795
+ message="Please select at least 1 seed to find locations of."
796
+ )
 
 
 
797
  return
798
 
799
  #get seeds from selected rows in table
 
849
 
850
  self.loc_finder_table.resizeColumnsToContents()
851
  except Exception as e:
852
+ show_error("Error in find_locations() in population analysis.", e)
 
 
 
 
 
 
 
 
 
 
 
 
853
 
854
  # this function clears the loc_finder_table
855
  def clear_loc_table(self):
 
857
  self.loc_finder_table.clearContents()
858
  self.loc_finder_table.setRowCount(0)
859
  except Exception as e:
860
+ show_error("Error in clear_loc_table() in population analysis.", e)
861
+
 
 
 
 
 
 
 
 
 
 
 
 
862
  # sorting function for table2 - shared seeds table: IE the table in top-right
863
  def table2_sorting(self, logicalIndex):
864
  try:
 
868
  else:
869
  self.table2.sortItems(logicalIndex, QtCore.Qt.AscendingOrder)
870
  except Exception as e:
871
+ show_error("Error in table2_sorting() in population analysis.", e)
 
 
 
 
 
 
 
 
 
 
 
 
872
 
873
  # sorting for location table: IE table in bottom right
874
  def loc_table_sorter(self, logicalIndex):
 
879
  else:
880
  self.loc_finder_table.sortItems(logicalIndex, QtCore.Qt.AscendingOrder)
881
  except Exception as e:
882
+ show_error("Error in loc_table_sorter() in population analysis.", e)
883
+
 
 
 
 
 
 
 
 
 
 
 
 
884
  #clears the table showcasing shared seeds
885
  def clear(self):
886
  try:
887
  self.table2.setRowCount(0)
888
  except Exception as e:
889
+ show_error("Error in clear() in population analysis.", e)
890
+
 
 
 
 
 
 
 
 
 
 
 
 
891
  #return to main function
892
  def go_back(self):
893
  try:
894
  GlobalSettings.mainWindow.show()
895
  self.hide()
896
  except Exception as e:
897
+ show_error("Error in go_back() in population analysis.", e)
 
 
 
 
 
 
 
 
 
 
 
 
898
 
899
  # this function calls the close window class. Allows the user to choose what files they want to keep/delete
900
  def closeEvent(self, event):
 
902
  GlobalSettings.mainWindow.closeFunction()
903
  event.accept()
904
  except Exception as e:
905
+ show_error("Error in closeEvent() in population analysis.", e)
906
+
 
 
 
 
 
 
 
 
 
 
 
 
 
907
  #loading window UI class for when data is loading
908
  class loading_window(QtWidgets.QMainWindow):
909
  def __init__(self):
910
  try:
911
  super(loading_window, self).__init__()
912
+ uic.loadUi(GlobalSettings.appdir + "ui/loading_data_form.ui", self)
913
  self.loading_bar.setValue(0)
914
  self.setWindowTitle("Loading Data")
915
  self.setWindowIcon(Qt.QIcon(GlobalSettings.appdir + "cas9image.ico"))
916
+ scale_ui(self, base_width=1920, base_height=1080, font_size=12, header_font_size=30, custom_scale_width=450, custom_scale_height=125)
917
  except Exception as e:
918
+ show_error("Error initializing loading_window class in population analysis.", e)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
919
 
920
  #matplotlib canvas class for the heatmap graph
921
  class MplCanvas(FigureCanvasQTAgg):
 
926
  self.axes.clear()
927
  super(MplCanvas, self).__init__(fig)
928
  except Exception as e:
929
+ show_error("Error initializing MplCanvas class in population analysis.", e)
 
 
 
 
 
 
 
 
 
 
 
 
scoring_window.py → controllers/scoring_window.py RENAMED
@@ -1,5 +1,5 @@
1
- from Algorithms import get_table_headers
2
- import GlobalSettings
3
  import azimuth.model_comparison as az
4
  from PyQt5 import QtWidgets, uic, QtCore
5
  import platform
@@ -50,7 +50,7 @@ class Scoring_Window(QtWidgets.QMainWindow):
50
  try:
51
  # qt stuff
52
  super(Scoring_Window, self).__init__()
53
- uic.loadUi(GlobalSettings.appdir + 'scoring_window.ui', self)
54
  self.progressBar.setValue(0) # Make sure the progress bar starts at 0
55
 
56
  # Button Connections
 
1
+ from utils.Algorithms import get_table_headers
2
+ import models.GlobalSettings as GlobalSettings
3
  import azimuth.model_comparison as az
4
  from PyQt5 import QtWidgets, uic, QtCore
5
  import platform
 
50
  try:
51
  # qt stuff
52
  super(Scoring_Window, self).__init__()
53
+ uic.loadUi(GlobalSettings.appdir + 'ui/scoring_window.ui', self)
54
  self.progressBar.setValue(0) # Make sure the progress bar starts at 0
55
 
56
  # Button Connections
error_handling.py DELETED
@@ -1,21 +0,0 @@
1
- from PyQt5 import QtWidgets
2
- import traceback
3
- import GlobalSettings
4
- from common_utils import show_message
5
-
6
- #global logger
7
- logger = GlobalSettings.logger
8
-
9
- def show_error(message, e):
10
- logger.critical(message)
11
- logger.critical(e)
12
- logger.critical(traceback.format_exc())
13
-
14
- show_message(
15
- fontSize=12,
16
- icon=QtWidgets.QMessageBox.Icon.Critical,
17
- title="Fatal Error",
18
- message=f"Fatal Error:\n{str(e)}\n\nFor more information on this error, look at CASPER.log in the application folder."
19
- )
20
-
21
- exit(-1)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
logs/CASPER.log DELETED
File without changes
main.py CHANGED
@@ -1,1650 +1,24 @@
1
- from Algorithms import get_table_headers
2
  import sys
3
  import os
4
- import io
5
  from PyQt5 import QtWidgets, Qt, QtGui, QtCore, uic
6
- from CoTargeting import CoTargeting
7
- from closingWin import closingWindow
8
- from Results import Results
9
- from NewGenome import NewGenome
10
- from NewEndonuclease import NewEndonuclease
11
- import genomeBrowser
12
- import webbrowser
13
- import requests
14
- import GlobalSettings
15
- import multitargeting
16
- from AnnotationParser import Annotation_Parser
17
- from export_tool import export_tool
18
- from generateLib import genLibrary
19
- from CSPRparser import CSPRparser
20
- import populationAnalysis
21
  import platform
22
- import ncbi
23
- import glob
24
- import traceback
25
- import math
26
  import logging
27
- from annotation_functions import *
28
- from error_handling import show_error
29
- from common_utils import show_message
30
- from ui_utils import scale_ui, center_ui
31
 
32
- #logger alias for global logger
33
  logger = GlobalSettings.logger
34
 
35
  fontSize = 12
36
 
37
- #Annotation file and search query from MainWindow
38
- class AnnotationsWindow(QtWidgets.QMainWindow):
39
- #init annotation window class
40
- def __init__(self, info_path):
41
- super(AnnotationsWindow, self).__init__()
42
- uic.loadUi(GlobalSettings.appdir + 'annotation_details.ui', self)
43
- self.setWindowIcon(Qt.QIcon(GlobalSettings.appdir + "cas9image.ico"))
44
- self.Submit_button.clicked.connect(self.submit)
45
- self.Go_Back_Button.clicked.connect(self.go_Back)
46
- self.select_all_checkbox.stateChanged.connect(self.select_all_genes)
47
- self.fontSize = 12 # Initialize fontSize
48
- self.mainWindow = ""
49
- self.type = ""
50
- self.mwfg = self.frameGeometry() ##Center window
51
- self.cp = QtWidgets.QDesktopWidget().availableGeometry().center() ##Center window
52
- self.switcher_table = [1, 1, 1, 1, 1, 1, 1, 1]
53
- self.tableWidget.setHorizontalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
54
- self.tableWidget.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
55
- self.tableWidget.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
56
- self.tableWidget.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
57
- self.tableWidget.setAutoScroll(False)
58
- self.tableWidget.horizontalHeader().sectionClicked.connect(self.table_sorting)
59
-
60
- scale_ui(self)
61
-
62
-
63
- def table_sorting(self, logicalIndex):
64
- try:
65
- self.switcher_table[logicalIndex] *= -1
66
- order = QtCore.Qt.DescendingOrder if self.switcher_table[logicalIndex] == -1 else QtCore.Qt.AscendingOrder
67
- self.tableWidget.sortItems(logicalIndex, order)
68
- except Exception as e:
69
- show_error("table_sorting() in Annotation Window", e)
70
-
71
- #submit selected rows for results to process
72
- def submit(self):
73
- try:
74
- self.mainWindow.collect_table_data_nonkegg()
75
- self.mainWindow.show() # Open main window back up
76
- except Exception as e:
77
- logger.critical("Error in submit() in AnnotationsWindow.")
78
- logger.critical(e)
79
- logger.critical(traceback.format_exc())
80
- show_error("submit() in AnnotationsWindow", e)
81
- finally:
82
- self.hide() # Close annotation window regardless of success or failure
83
-
84
- #go back to main
85
- def go_Back(self):
86
- try:
87
- self.tableWidget.clear()
88
- self.mainWindow.searches.clear()
89
- self.tableWidget.setColumnCount(0)
90
- self.mainWindow.show()
91
- self.mainWindow.progressBar.setValue(0)
92
- self.hide()
93
- except Exception as e:
94
- show_error("go_Back() in AnnotationsWindow", e)
95
- self.mainWindow.checkBoxes.clear()
96
-
97
- # this is the connection for the select all checkbox - selects/deselects all the genes in the table
98
- # this function is very similar to the other fill_table, it just works with the other types of annotation files
99
- def fill_table_nonKegg(self, mainWindow, results_list):
100
- try:
101
- self.tableWidget.clearContents()
102
- self.mainWindow = mainWindow
103
- self.tableWidget.setColumnCount(5)
104
- self.mainWindow.progressBar.setValue(85)
105
- self.tableWidget.setHorizontalHeaderLabels(["Feature Type", "Chromosome/Scaffold #", "Feature ID/Locus Tag", "Feature Name", "Feature Description"])
106
-
107
- mainWindow.checkBoxes = []
108
- self.type = "nonkegg"
109
-
110
- for index, result in enumerate(results_list[:1000]): # Limit to first 1000 results
111
- self.tableWidget.setRowCount(index + 1) # Increment table row count
112
- chrom, feature = result
113
-
114
- # Create and set table items
115
- items = [
116
- QtWidgets.QTableWidgetItem(feature.type),
117
- QtWidgets.QTableWidgetItem(str(chrom)),
118
- QtWidgets.QTableWidgetItem(get_id(feature)),
119
- QtWidgets.QTableWidgetItem(get_name(feature)),
120
- QtWidgets.QTableWidgetItem(get_description(feature))
121
- ]
122
-
123
- for col, item in enumerate(items):
124
- self.tableWidget.setItem(index, col, item)
125
-
126
- mainWindow.checkBoxes.append((chrom, feature, index))
127
-
128
- self.tableWidget.resizeColumnsToContents()
129
- mainWindow.hide()
130
-
131
- #center on current screen
132
- center_ui(self)
133
- self.show()
134
- self.activateWindow()
135
-
136
- return 0
137
- except Exception as e:
138
- show_error("fill_table_nonKegg() in AnnotationsWindow", e)
139
-
140
- # this is the connection for the select all checkbox - selects/deselects all the genes in the table
141
- def select_all_genes(self):
142
- try:
143
- if self.select_all_checkbox.isChecked():
144
- self.tableWidget.selectAll()
145
- else:
146
- self.tableWidget.clearSelection()
147
- except Exception as e:
148
- show_error("select_all_genes() in AnnotationsWindow", e)
149
-
150
- # this function calls the closingWindow class.
151
- def closeEvent(self, event):
152
- try:
153
- GlobalSettings.mainWindow.closeFunction()
154
- except Exception as e:
155
- show_error("closeEvent() in AnnotationsWindow", e)
156
- event.accept()
157
-
158
- # =========================================================================================
159
- # CLASS NAME: CMainWindow
160
- # Inputs: Takes in the path information from the startup window and also all input parameters
161
- # Outputs: The results of the target search process by generating a new Results window
162
- # =========================================================================================
163
- class CMainWindow(QtWidgets.QMainWindow):
164
-
165
- def __init__(self, info_path):
166
- super(CMainWindow, self).__init__()
167
- uic.loadUi(GlobalSettings.appdir + 'CASPER_main.ui', self)
168
- self.dbpath = ""
169
- self.inputstring = "" # This is the search string
170
- self.info_path = info_path
171
- self.anno_name = ""
172
- self.endo_name = ""
173
- self.fontSize = 12
174
- self.org = ""
175
- self.TNumbers = {} # the T numbers from a kegg search
176
- self.orgcodes = {} # Stores the Kegg organism code by the format {full name : organism code}
177
- self.gene_list = {} # list of genes (no ides what they pertain to
178
- self.searches = {}
179
- self.checkBoxes = []
180
- self.genlib_list = [] # This list stores selected SeqFeatures from annotation window
181
- self.checked_info = {}
182
- self.check_ntseq_info = {} # the ntsequences that go along with the checked_info
183
- self.annotation_parser = Annotation_Parser()
184
- self.link_list = list() # the list of the downloadable links from the NCBI search
185
- self.organismDict = dict() # the dictionary for the links to download. Key is the description of the organism, value is the ID that can be found in link_list
186
- self.results_list = list()
187
- self.organismData = list()
188
- self.ncbi = ncbi.NCBI_search_tool()
189
-
190
- groupbox_style = """
191
- QGroupBox:title{subcontrol-origin: margin;
192
- left: 10px;
193
- padding: 0 5px 0 5px;}
194
- QGroupBox#Step1{border: 2px solid rgb(111,181,110);
195
- border-radius: 9px;
196
- margin-top: 10px;
197
- font: bold 14pt 'Arial';}
198
- """
199
-
200
- self.Step1.setStyleSheet(groupbox_style)
201
- self.Step2.setStyleSheet(groupbox_style.replace("Step1", "Step2"))
202
- self.Step3.setStyleSheet(groupbox_style.replace("Step1", "Step3"))
203
- self.CASPER_Navigation.setStyleSheet(groupbox_style.replace("Step1", "CASPER_Navigation").replace("solid","dashed").replace("rgb(111,181,110)","rgb(88,89,91)"))
204
-
205
- self.setWindowIcon(Qt.QIcon(GlobalSettings.appdir + "cas9image.ico"))
206
- self.pushButton_FindTargets.clicked.connect(self.gather_settings)
207
- self.pushButton_ViewTargets.clicked.connect(self.view_results)
208
- self.pushButton_ViewTargets.setEnabled(False)
209
- self.GenerateLibrary.setEnabled(False)
210
- self.radioButton_Gene.clicked.connect(self.toggle_annotation)
211
- self.radioButton_Position.clicked.connect(self.toggle_annotation)
212
-
213
- """ Connect functions to buttons """
214
- self.newGenome_button.clicked.connect(self.launch_newGenome) # Connect launch function to New Genome
215
- self.newEndo_button.clicked.connect(self.launch_newEndonuclease) # Connect launch function to New Endonuclease
216
- self.multitargeting_button.clicked.connect(self.changeto_multitargeting) # Connect launch function to Multitargeting
217
- self.populationAnalysis_button.clicked.connect(self.changeto_population_Analysis) # Connect launch function to PA
218
- self.GenerateLibrary.clicked.connect(self.prep_genlib)
219
-
220
- """ Connect functions to actions (menu bar) """
221
- self.actionOpen_Genome_Browser.triggered.connect(self.launch_newGenomeBrowser)
222
- self.actionExit.triggered.connect(self.close_app)
223
- self.visit_repo.triggered.connect(self.visit_repo_func)
224
- self.actionChange_Directory.triggered.connect(self.change_directory)
225
- self.actionNCBI.triggered.connect(self.open_ncbi_web_page)
226
- # self.actionCasper2.triggered.connect(self.open_casper2_web_page)
227
- self.actionNCBI_BLAST.triggered.connect(self.open_ncbi_blast_web_page)
228
-
229
-
230
-
231
- self.progressBar.setMinimum(0)
232
- self.progressBar.setMaximum(100)
233
- self.progressBar.reset()
234
- self.Annotation_Window = AnnotationsWindow(info_path)
235
- self.geneEntryField.setPlaceholderText("Example Inputs: \n\n"
236
- "Option 1: Feature (ID, Locus Tag, or Name)\n"
237
- "Example: 854068/YOL086C/ADH1 for S. cerevisiae alcohol dehydrogenase 1\n\n"
238
- "Option 2: Position (chromosome,start,stop)\n"
239
- "Example: 1,1,1000 for targeting chromosome 1, base pairs 1 to 1000\n\n"
240
- "Option 3: Sequence (must be within the selected organism)\n"
241
- "Example: Any nucleotide sequence between 100 and 10,000 base pairs.\n\n"
242
- "*Note: to multiplex, separate multiple queries by new lines*\n"
243
- "Example:\n"
244
- "1,1,1000\n"
245
- "5,1,500\n"
246
- "etc.")
247
-
248
- # show functionalities on window
249
- self.newGenome = NewGenome(info_path)
250
- self.newEndonuclease = NewEndonuclease()
251
- self.CoTargeting = CoTargeting(info_path)
252
- self.Results = Results()
253
- self.export_tool_window = export_tool()
254
- self.genLib = genLibrary()
255
- self.myClosingWindow = closingWindow()
256
- self.genomebrowser = genomeBrowser.genomebrowser()
257
- self.launch_ncbi_button.clicked.connect(self.launch_ncbi)
258
-
259
- #scale UI
260
- self.first_show = True
261
- scale_ui(self)
262
-
263
- # this function prepares everything for the generate library function
264
- # it is very similar to the gather settings, how ever it stores the data instead of calling the Annotation Window class
265
- # it moves the data onto the generateLib function, and then opens that window
266
- def prep_genlib(self):
267
- # make sure the user actually inputs something
268
- try:
269
- inputstring = str(self.geneEntryField.toPlainText())
270
- if (inputstring.startswith("Example Inputs:") or inputstring == ""):
271
- msgBox = QtWidgets.QMessageBox()
272
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
273
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
274
- msgBox.setWindowTitle("Error")
275
- msgBox.setText("No gene has been entered. Please enter a gene.")
276
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
277
- msgBox.exec()
278
-
279
- else:
280
- # standardize the input
281
- inputstring = inputstring.lower()
282
- found_matches_bool = True
283
- # call the respective function
284
- self.progressBar.setValue(10)
285
- if self.radioButton_Gene.isChecked():
286
- if len(self.genlib_list) > 0:
287
- found_matches_bool = True
288
- else:
289
- found_matches_bool = False
290
- elif self.radioButton_Position.isChecked() or self.radioButton_Sequence.isChecked():
291
- msgBox = QtWidgets.QMessageBox()
292
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
293
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
294
- msgBox.setWindowTitle("Error")
295
- msgBox.setText("Generate Library can only work with feature searches.")
296
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
297
- msgBox.exec()
298
-
299
- return
300
- """
301
- elif self.radioButton_Position.isChecked():
302
- pinput = inputstring.split(';')
303
- found_matches_bool = self.run_results("position", pinput,openAnnoWindow=False)
304
- elif self.radioButton_Sequence.isChecked():
305
- sinput = inputstring
306
- found_matches_bool = self.run_results("sequence", sinput, openAnnoWindow=False)
307
- """
308
- # if matches are found
309
- if found_matches_bool == True:
310
- # get the cspr file name
311
- cspr_file = self.organisms_to_files[self.orgChoice.currentText()][self.endoChoice.currentText()][0]
312
- if platform.system() == 'Windows':
313
- cspr_file = GlobalSettings.CSPR_DB + '\\' + cspr_file
314
- else:
315
- cspr_file = GlobalSettings.CSPR_DB + '/' + cspr_file
316
- kegg_non = 'non_kegg'
317
-
318
- # launch generateLib
319
- self.progressBar.setValue(100)
320
-
321
- # calculate the total number of matches found
322
- tempSum = len(self.genlib_list)
323
-
324
- # warn the user if the number is greater than 50
325
- if tempSum > 50:
326
- msgBox = QtWidgets.QMessageBox()
327
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
328
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Question)
329
- msgBox.setWindowTitle("Many Matches Found")
330
- msgBox.setText("More than 50 matches have been found. Continuing could cause a slow down...\n\n Do you wish to continue?")
331
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Yes)
332
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.No)
333
- msgBox.exec()
334
-
335
- if (msgBox.result() == QtWidgets.QMessageBox.No):
336
- self.searches.clear()
337
- self.progressBar.setValue(0)
338
- return -2
339
-
340
- self.genLib.launch(self.genlib_list,cspr_file, kegg_non)
341
- else:
342
- self.progressBar.setValue(0)
343
- except Exception as e:
344
- logger.critical("Error in prep_genlib() in main.")
345
- logger.critical(e)
346
- logger.critical(traceback.format_exc())
347
-
348
- #print("prep genlib")
349
- msgBox = QtWidgets.QMessageBox()
350
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
351
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
352
- msgBox.setWindowTitle("Fatal Error")
353
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
354
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
355
- msgBox.exec()
356
-
357
- exit(-1)
358
-
359
- # Function for collecting the settings from the input field and transferring them to run_results
360
- def gather_settings(self):
361
- try:
362
- ### If user searches multiple times for the same thing, this avoids re-searching the entire annotation file
363
- check_org = self.orgChoice.currentText().lower()
364
- check_endo = self.endoChoice.currentText().lower()
365
- check_anno_name = self.annotation_files.currentText().lower()
366
- check_input = str(self.geneEntryField.toPlainText()).lower()
367
- if (check_input == self.inputstring and check_org == self.org and check_anno_name == self.anno_name and check_endo == self.endo_name):
368
- same_search = True
369
- else:
370
- self.org = check_org
371
- self.anno_name = check_anno_name
372
- self.inputstring = check_input
373
- self.endo_name = check_endo
374
- same_search = False
375
-
376
- # Error check: make sure the user actually inputs something
377
- if (self.inputstring.startswith("Example Inputs:") or self.inputstring == ""):
378
- msgBox = QtWidgets.QMessageBox()
379
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
380
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
381
- msgBox.setWindowTitle("Error")
382
- msgBox.setText(
383
- "No feature has been searched for. Please enter a search.")
384
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
385
- msgBox.exec()
386
-
387
- else:
388
-
389
- ### Remove additional scoring columns if necessary
390
- header = get_table_headers(self.Results.targetTable) # Returns headers of the target table in View Targets window
391
- col_indices = [header.index(x) for x in GlobalSettings.algorithms if x in header] # Returns the index(es) of the alternative scoring column(s) in the target table of View Targets window
392
- if len(col_indices) > 0: # If alternative scoring has been done
393
- for i in col_indices:
394
- self.Results.targetTable.removeColumn(i)
395
- self.Results.targetTable.resizeColumnsToContents()
396
-
397
- self.progressBar.setValue(10)
398
- if self.radioButton_Gene.isChecked():
399
- ginput = [x.strip() for x in self.inputstring.split('\n')] # Split search based on newline character and remove deadspace
400
- self.run_results("feature", ginput, same_search)
401
- elif self.radioButton_Position.isChecked():
402
- pinput = [x.strip() for x in self.inputstring.split('\n')] # Split search based on newline character and remove deadspace
403
- self.run_results("position", pinput, same_search)
404
- elif self.radioButton_Sequence.isChecked():
405
- sinput = self.inputstring
406
- self.run_results("sequence", sinput, same_search)
407
- except Exception as e:
408
- logger.critical("Error in gather_settings() in main.")
409
- logger.critical(e)
410
- logger.critical(traceback.format_exc())
411
- msgBox = QtWidgets.QMessageBox()
412
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
413
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
414
- msgBox.setWindowTitle("Fatal Error")
415
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
416
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
417
- msgBox.exec()
418
- msgBox = QtWidgets.QMessageBox()
419
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
420
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
421
- msgBox.setWindowTitle("Fatal Error")
422
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
423
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
424
- msgBox.exec()
425
-
426
- exit(-1)
427
-
428
- # ---- Following functions are for running the auxillary algorithms and windows ---- #
429
- # this function is parses the annotation file given, and then goes through and goes onto results
430
- # it will call other versions of collect_table_data and fill_table that work with these file types
431
- # this function should work with the any type of annotation file, besides kegg.
432
- # this assumes that the parsers all store the data the same way, which gff and feature table do
433
- # please make sure the genbank parser stores the data in the same way
434
- # so far the gff files seems to all be different. Need to think about how we want to parse it
435
- def run_results_own_ncbi_file(self, inputstring, fileName, same_search, openAnnoWindow=True):
436
- try:
437
- self.set_progress(35)
438
- self.results_list = self.annotation_parser.genbank_search(inputstring, same_search)
439
-
440
- cspr_file = self.organisms_to_files[self.orgChoice.currentText()][self.endoChoice.currentText()][0]
441
- cspr_file = os.path.join(GlobalSettings.CSPR_DB, cspr_file)
442
-
443
- own_cspr_parser = CSPRparser(cspr_file)
444
- own_cspr_parser.read_first_lines()
445
- if len(own_cspr_parser.karystatsList) != self.annotation_parser.max_chrom:
446
- show_message(
447
- fontSize=12,
448
- icon=QtWidgets.QMessageBox.Icon.Warning,
449
- title="Warning:",
450
- message="The number of chromosomes do not match. This could cause errors.",
451
- button=QtWidgets.QMessageBox.StandardButton.Ok
452
- )
453
- self.set_progress(60)
454
-
455
- self.searches.clear()
456
-
457
- self.set_progress(75)
458
- if not self.results_list:
459
- show_message(
460
- fontSize=12,
461
- icon=QtWidgets.QMessageBox.Icon.Critical,
462
- title="No Matches Found",
463
- message="No matches found with that search, please try again.",
464
- button=QtWidgets.QMessageBox.StandardButton.Ok
465
- )
466
- self.set_progress(0)
467
- return False if not openAnnoWindow else None
468
-
469
- self.set_progress(80)
470
-
471
- return self.Annotation_Window.fill_table_nonKegg(self, self.results_list) if openAnnoWindow else True
472
- except Exception as e:
473
- show_error(f"Error in run_results_own_ncbi_file() in main.", e)
474
-
475
- def set_progress(self, value):
476
- self.progressBar.setValue(value)
477
-
478
- def run_results(self, inputtype, inputstring, same_search, openAnnoWindow=True):
479
- try:
480
- file_name = self.annotation_files.currentText()
481
- for file in glob.glob(GlobalSettings.CSPR_DB + "/**/*.gb*", recursive=True):
482
- if file_name in file:
483
- self.annotation_parser.annotationFileName = file
484
- break
485
- self.Results.annotation_path = self.annotation_parser.annotationFileName
486
-
487
- progvalue = 15
488
- self.searches = {}
489
- self.gene_list = {}
490
- self.progressBar.setValue(progvalue)
491
-
492
- try:
493
- self.Results.endonucleaseBox.currentIndexChanged.disconnect()
494
- except Exception as e:
495
- pass
496
- # set Results endo combo box
497
- self.Results.endonucleaseBox.clear()
498
-
499
- # set the results window endoChoice box menu
500
- # set the mainWindow's endoChoice first, and then loop through and set the rest of them
501
- self.Results.endonucleaseBox.addItem(self.endoChoice.currentText())
502
- for item in self.organisms_to_endos[str(self.orgChoice.currentText())]:
503
- if item != self.Results.endonucleaseBox.currentText():
504
- self.Results.endonucleaseBox.addItem(item)
505
-
506
- self.Results.endonucleaseBox.currentIndexChanged.connect(self.Results.changeEndonuclease)
507
- self.Results.get_endo_data()
508
-
509
- # self.Results.change_start_end_button.setEnabled(False)
510
- self.Results.displayGeneViewer.setChecked(0)
511
-
512
- if inputtype == "feature":
513
-
514
- fileType = self.annotation_parser.find_which_file_version()
515
-
516
- # if the parser retuns the 'wrong file type' error
517
- if fileType == -1:
518
- msgBox = QtWidgets.QMessageBox()
519
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
520
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
521
- msgBox.setWindowTitle("Error:")
522
- msgBox.setText("Feature search requires a GenBank formatted annotation file. Please select a file from the dropdown menu or search by position")
523
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
524
- msgBox.exec()
525
-
526
- self.progressBar.setValue(0)
527
- return
528
-
529
- # make sure an annotation file has been selected
530
- if self.annotation_files.currentText() == "None":
531
- msgBox = QtWidgets.QMessageBox()
532
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
533
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
534
- msgBox.setWindowTitle("No Annotation")
535
- msgBox.setText("Search by feature requires a GenBank annotation file. Please select one from the dropdown menu or search by position.")
536
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
537
- msgBox.exec()
538
-
539
- self.progressBar.setValue(0)
540
- return
541
- # this now just goes onto the other version of run_results
542
- myBool = self.run_results_own_ncbi_file(inputstring, self.annotation_files.currentText(), same_search, openAnnoWindow=openAnnoWindow)
543
- if not openAnnoWindow:
544
- return myBool
545
- else:
546
- self.progressBar.setValue(0)
547
- return
548
-
549
- # position code below
550
- if inputtype == "position":
551
- full_org = str(self.orgChoice.currentText())
552
- self.checked_info.clear()
553
- self.check_ntseq_info.clear()
554
-
555
- for item in inputstring: # Loop through each search
556
- searchIndices = [x.strip() for x in item.split(',')] # Parse input query
557
- ### Make sure the right amount of arguments were passed
558
- if len(searchIndices) != 3:
559
- msgBox = QtWidgets.QMessageBox()
560
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
561
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
562
- msgBox.setWindowTitle("Position Error: Invalid Input")
563
- msgBox.setText(
564
- "There are 3 arguments required for this function: chromosome, start position, and end position.")
565
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
566
- msgBox.exec()
567
-
568
- self.progressBar.setValue(0)
569
- return
570
-
571
- ### Make sure user inputs digits
572
- if not searchIndices[0].isdigit() or not searchIndices[1].isdigit() or not searchIndices[2].isdigit():
573
- msgBox = QtWidgets.QMessageBox()
574
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
575
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
576
- msgBox.setWindowTitle("Position Error: Invalid Input")
577
- msgBox.setText(
578
- "The positions given must be integers. Please try again.")
579
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
580
- msgBox.exec()
581
-
582
- self.progressBar.setValue(0)
583
- return
584
-
585
- ### Make sure start is less than end
586
- elif int(searchIndices[1]) >= int(searchIndices[2]):
587
- msgBox = QtWidgets.QMessageBox()
588
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
589
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
590
- msgBox.setWindowTitle("Position Error: Start Must Be Less Than End")
591
- msgBox.setText(
592
- "The start index must be less than the end index.")
593
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
594
- msgBox.exec()
595
-
596
- self.progressBar.setValue(0)
597
- return
598
-
599
- ### Make sure range isn't too large
600
- elif abs(int(searchIndices[2])-int(searchIndices[1])) > 50000:
601
- msgBox = QtWidgets.QMessageBox()
602
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
603
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
604
- msgBox.setWindowTitle("Position Error: Range Too Large")
605
- msgBox.setText(
606
- "The search range must be less than 50,000 nt.")
607
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
608
- msgBox.exec()
609
-
610
- self.progressBar.setValue(0)
611
- return
612
-
613
- ### Make sure chromosome exists
614
- elif int(searchIndices[0]) > self.annotation_parser.get_max_chrom():
615
- msgBox = QtWidgets.QMessageBox()
616
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
617
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
618
- msgBox.setWindowTitle("Position Error: Chromsome Doesn't Exist")
619
- msgBox.setText(
620
- "Chromosome %s does not exist in the selected annotation file." % searchIndices[0])
621
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
622
- msgBox.exec()
623
-
624
- self.progressBar.setValue(0)
625
- return
626
-
627
- # append the data into the checked_info
628
- tempString = 'chrom: ' + str(searchIndices[0]) + ',start: ' + str(searchIndices[1]) + ',end: ' + str(searchIndices[2])
629
- self.checked_info[tempString] = (int(searchIndices[0]), int(searchIndices[1])-1, int(searchIndices[2]))
630
-
631
- self.progressBar.setValue(50)
632
- self.Results.transfer_data(full_org, self.organisms_to_files[full_org], [str(self.endoChoice.currentText())], os.getcwd(), self.checked_info, self.check_ntseq_info,inputtype)
633
- self.Results.load_gene_viewer()
634
- self.progressBar.setValue(100)
635
- self.pushButton_ViewTargets.setEnabled(True)
636
- self.GenerateLibrary.setEnabled(True)
637
-
638
- # sequence code below
639
- if inputtype == "sequence":
640
- fileType = self.annotation_parser.find_which_file_version()
641
- # if the parser retuns the 'wrong file type' error
642
- if fileType == -1:
643
- msgBox = QtWidgets.QMessageBox()
644
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
645
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
646
- msgBox.setWindowTitle("Error:")
647
- msgBox.setText("Search by sequence requires a GenBank annotation file. Please select one from the dropdown menu or search by position.")
648
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
649
- msgBox.exec()
650
-
651
- self.progressBar.setValue(0)
652
- return
653
- if self.annotation_files.currentText() == "None":
654
- msgBox = QtWidgets.QMessageBox()
655
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
656
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
657
- msgBox.setWindowTitle("Error:")
658
- msgBox.setText("Search by sequence requires a GenBank annotation file. Please select one from the dropdown menu or search by position.")
659
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
660
- msgBox.exec()
661
-
662
- self.progressBar.setValue(0)
663
- return
664
-
665
-
666
- checkString = 'AGTCN'
667
- full_org = str(self.orgChoice.currentText())
668
- self.checked_info.clear()
669
- self.progressBar.setValue(10)
670
- inputstring = inputstring.replace('\n','').upper().strip()
671
-
672
- # make sure all the chars are one of A, G, T, C, or N
673
- for letter in inputstring:
674
- if letter not in checkString:
675
- msgBox = QtWidgets.QMessageBox()
676
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
677
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
678
- msgBox.setWindowTitle("Sequence Error")
679
- msgBox.setText(
680
- "The sequence must consist of A, G, T, C, or N. No other characters are allowed.")
681
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
682
- msgBox.exec()
683
-
684
- self.progressBar.setValue(0)
685
- return
686
-
687
- # check to make sure that the use gave a long enough sequence
688
- if len(inputstring) < 100:
689
- msgBox = QtWidgets.QMessageBox()
690
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
691
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
692
- msgBox.setWindowTitle("Error")
693
- msgBox.setText(
694
- "The sequence given is too small. At least 100 characters are required.")
695
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
696
- msgBox.exec()
697
-
698
- self.progressBar.setValue(0)
699
- return
700
-
701
- # give a warning if the length of the sequence is long
702
- if len(inputstring) > 10000:
703
- msgBox = QtWidgets.QMessageBox()
704
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
705
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Question)
706
- msgBox.setWindowTitle("Large Sequence Detected")
707
- msgBox.setText(
708
- "The sequence given is too large one.\n\nPlease input a sequence less than 10kb in length.")
709
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Yes)
710
- msgBox.exec()
711
-
712
- self.progressBar.setValue(0)
713
- return
714
-
715
- self.progressBar.setValue(30)
716
-
717
- # Check the GBFF file for the sequence
718
- my_check = self.annotation_parser.get_sequence_info(inputstring)
719
-
720
- self.progressBar.setValue(55) # Update progress bar
721
-
722
- if type(my_check) == bool: # This means the sequence was not found
723
- msgBox = QtWidgets.QMessageBox()
724
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
725
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Question)
726
- msgBox.setWindowTitle("Sequence Not Found")
727
- msgBox.setText(
728
- "The sequence entered was not found.\n\nPlease input a sequence that is in the selected organism.")
729
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Yes)
730
- msgBox.exec()
731
- self.progressBar.setValue(0)
732
- return
733
-
734
- else: # This means the sequence was found
735
- # append the data into the checked_info
736
- tempString = 'chrom: ' + str(my_check[0]) + ',start: ' + str(my_check[1]) + ',end: ' + str(my_check[2])
737
- self.checked_info[tempString] = (int(my_check[0]), int(my_check[1])-1, int(my_check[2]))
738
-
739
- self.progressBar.setValue(75) # Update progress bar
740
-
741
- self.Results.transfer_data(full_org, self.organisms_to_files[full_org], [str(self.endoChoice.currentText())], os.getcwd(), self.checked_info, self.check_ntseq_info, inputtype)
742
- self.Results.load_gene_viewer()
743
- self.progressBar.setValue(100)
744
- self.pushButton_ViewTargets.setEnabled(True)
745
- self.GenerateLibrary.setEnabled(True)
746
-
747
-
748
- except Exception as e:
749
- logger.critical("Error in run_results() in main.")
750
- logger.critical(e)
751
- logger.critical(traceback.format_exc())
752
- msgBox = QtWidgets.QMessageBox()
753
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
754
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
755
- msgBox.setWindowTitle("Fatal Error")
756
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
757
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
758
- msgBox.exec()
759
-
760
- exit(-1)
761
-
762
- def handle_feature_search(self, input_string, open_anno_window):
763
- file_type = self.annotation_parser.find_which_file_version()
764
- if file_type == -1 or self.annotation_files.currentText() == "None":
765
- self.show_error_message("Feature search requires a GenBank formatted annotation file.")
766
- return False
767
-
768
- return self.run_results_own_ncbi_file(input_string, self.annotation_files.currentText(), same_search, open_anno_window)
769
-
770
-
771
- def launch_newGenome(self):
772
- try:
773
- # Update endo list
774
- self.newGenome.fillEndo()
775
- if self.newGenome.first_show:
776
- center_ui(self.newGenome)
777
- self.newGenome.first_show = False
778
- self.hide()
779
- self.newGenome.show()
780
- except Exception as e:
781
- show_error("launch_newGenome() in main", e)
782
-
783
- #launch new endo tool
784
- def launch_newEndonuclease(self):
785
- try:
786
- # Initialize and display the new endonuclease window
787
- self.newEndonuclease.centerUI()
788
- self.newEndonuclease.show()
789
- self.newEndonuclease.activateWindow()
790
- except Exception as e:
791
- show_error("launch_newEndonuclease() in main", e)
792
-
793
- #launch genome browser tool
794
- def launch_newGenomeBrowser(self):
795
- try:
796
- self.genomebrowser.createGraph(self)
797
- except Exception as e:
798
- show_error("launch_newGenomeBrowser() in main", e)
799
-
800
- #launch ncbi tool
801
- def launch_ncbi(self):
802
- try:
803
- msgBox = QtWidgets.QMessageBox()
804
- msgBox.setStyleSheet(f"font: {self.fontSize}pt 'Arial'")
805
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Information)
806
- msgBox.setWindowTitle("Note:")
807
- msgBox.setText(
808
- "NCBI Annotation Guidelines:\n\nDownload annotation files of the exact species and strain used in Analyze New Genome.\n\nMismatched annotation files will inhibit downstream analyses.")
809
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
810
- msgBox.exec()
811
-
812
- if self.ncbi.first_show:
813
- self.ncbi.first_show = False
814
- self.ncbi.centerUI()
815
-
816
- self.ncbi.show()
817
- self.ncbi.activateWindow()
818
- except Exception as e:
819
- show_error("launch_ncbi() in main", e)
820
-
821
- # this function does the same stuff that the other collect_table_data does, but works with the other types of files
822
- def collect_table_data_nonkegg(self):
823
- try:
824
- # start out the same as the other collect_table_data
825
- self.checked_info.clear()
826
- self.genlib_list.clear()
827
- self.check_ntseq_info.clear()
828
- full_org = str(self.orgChoice.currentText())
829
- holder = ()
830
- selected_indices = []
831
- selected_rows = self.Annotation_Window.tableWidget.selectionModel().selectedRows()
832
- for ind in sorted(selected_rows):
833
- selected_indices.append(ind.row())
834
-
835
- for item in self.checkBoxes:
836
- feature = item[1]
837
- # If inidices of checkBoxes list and selected rows in table match...
838
- if item[2] in selected_indices:
839
- holder = (item[0],int(feature.location.start),int(feature.location.end)) # Tuple order: Feature chromosome/scaffold number, feature start, feature end
840
- ### If locus tag available, combine with gene name to create dict key
841
- if 'locus_tag' in feature.qualifiers:
842
- tag = feature.qualifiers['locus_tag'][0]
843
- key = tag + ": " + get_name(feature)
844
- else:
845
- key = get_name(feature)
846
- self.checked_info[key] = holder
847
- self.genlib_list.append((item[0],feature)) # Tuple order: Feature chromosome/scaffold number, SeqFeature object
848
- else:
849
- # If item was not selected in the table, go to the next item
850
- continue
851
-
852
- # now call transfer data
853
- self.progressBar.setValue(95)
854
- self.Results.transfer_data(full_org, self.organisms_to_files[full_org], [str(self.endoChoice.currentText())], os.getcwd(),
855
- self.checked_info, self.check_ntseq_info,inputtype="feature")
856
- self.Results.load_gene_viewer()
857
-
858
- self.progressBar.setValue(100)
859
- self.pushButton_ViewTargets.setEnabled(True)
860
- self.GenerateLibrary.setEnabled(True)
861
- except Exception as e:
862
- logger.critical("Error in collect_table_data_nonkegg() in main.")
863
- logger.critical(e)
864
- logger.critical(traceback.format_exc())
865
- msgBox = QtWidgets.QMessageBox()
866
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
867
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
868
- msgBox.setWindowTitle("Fatal Error")
869
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
870
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
871
- msgBox.exec()
872
-
873
- exit(-1)
874
-
875
- def separate_line(self, input_string):
876
- try:
877
- export_array = []
878
- while True:
879
- index = input_string.find('\n')
880
- if index == -1:
881
- if len(input_string) == 0:
882
- return export_array
883
- else:
884
- export_array.append(input_string)
885
- return export_array
886
- export_array.append(input_string[:index])
887
- input_string = input_string[index + 1:]
888
- except Exception as e:
889
- logger.critical("Error in seperate_line() in main.")
890
- logger.critical(e)
891
- logger.critical(traceback.format_exc())
892
- msgBox = QtWidgets.QMessageBox()
893
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
894
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
895
- msgBox.setWindowTitle("Fatal Error")
896
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
897
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
898
- msgBox.exec()
899
-
900
- exit(-1)
901
-
902
- def removeWhiteSpace(self, strng):
903
- try:
904
- while True:
905
- if len(strng) == 0 or (strng[0] != " " and strng[0] != "\n"):
906
- break
907
- strng = strng[1:]
908
- while True:
909
- if len(strng) == 0 or (strng[len(strng) - 1] != " " and strng[0] != "\n"):
910
- return strng
911
- strng = strng[:len(strng) - 1]
912
- except Exception as e:
913
- logger.critical("Error in removeWhiteSpace() in main.")
914
- logger.critical(e)
915
- logger.critical(traceback.format_exc())
916
- msgBox = QtWidgets.QMessageBox()
917
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
918
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
919
- msgBox.setWindowTitle("Fatal Error")
920
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
921
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
922
- msgBox.exec()
923
-
924
- exit(-1)
925
-
926
- # Function to enable and disable the Annotation function if searching by position or sequence
927
- def toggle_annotation(self):
928
- try:
929
- if self.radioButton_Gene.isChecked():
930
- self.Step2.setEnabled(True)
931
- else:
932
- self.Step2.setEnabled(True)
933
- except Exception as e:
934
- logger.critical("Error in toggle_annotation() in main.")
935
- logger.critical(e)
936
- logger.critical(traceback.format_exc())
937
- msgBox = QtWidgets.QMessageBox()
938
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
939
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
940
- msgBox.setWindowTitle("Fatal Error")
941
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
942
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
943
- msgBox.exec()
944
-
945
- exit(-1)
946
-
947
- def fill_annotation_dropdown(self):
948
- try:
949
- #recursive search for all GenBank files in casper db folder
950
- self.annotation_files.clear()
951
- annotation_files = glob.glob(GlobalSettings.CSPR_DB + "/**/*.gb*", recursive=True)
952
- if platform.system() == "Windows":
953
- for i in range(len(annotation_files)):
954
- annotation_files[i] = annotation_files[i].replace("/","\\")
955
- annotation_files[i] = annotation_files[i][annotation_files[i].rfind("\\") + 1:]
956
- else:
957
- for i in range(len(annotation_files)):
958
- annotation_files[i] = annotation_files[i].replace("\\","/")
959
- annotation_files[i] = annotation_files[i][annotation_files[i].rfind("/") + 1:]
960
-
961
- annotation_files.sort(key=str.lower)
962
- self.annotation_files.addItems(annotation_files)
963
- self.annotation_files.addItems(["None"])
964
- except Exception as e:
965
- logger.critical("Error in fill_annotation_dropdown() in main.")
966
- logger.critical(e)
967
- logger.critical(traceback.format_exc())
968
- msgBox = QtWidgets.QMessageBox()
969
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
970
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
971
- msgBox.setWindowTitle("Fatal Error")
972
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
973
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
974
- msgBox.exec()
975
-
976
- exit(-1)
977
-
978
- def make_dictonary(self):
979
- try:
980
- url = "https://www.genome.jp/dbget-bin/get_linkdb?-t+genes+gn:" + self.TNumbers[
981
- self.Annotations_Organism.currentText()]
982
- source_code = requests.get(url, verify=False)
983
- plain_text = source_code.text
984
- buf = io.StringIO(plain_text)
985
-
986
- while True:
987
- line = buf.readline()
988
- if line[0] == "-":
989
- break
990
- while True:
991
- line = buf.readline()
992
- if line[1] != "a":
993
- return
994
- line = line[line.find(">") + 1:]
995
- seq = line[line.find(":") + 1:line.find("<")]
996
- line = line[line.find(">") + 1:]
997
-
998
- i = 0
999
- while True:
1000
- if line[i] == " ":
1001
- i = i + 1
1002
- else:
1003
- break
1004
- key = line[i:line.find("\n") - 1]
1005
- if key in self.gene_list:
1006
- if seq not in self.gene_list[key]:
1007
- self.gene_list[key].append(seq)
1008
- else:
1009
- self.gene_list[key] = [seq]
1010
- z = 5
1011
- except Exception as e:
1012
- logger.critical("Error in make_dictionary() in main.")
1013
- logger.critical(e)
1014
- logger.critical(traceback.format_exc())
1015
- msgBox = QtWidgets.QMessageBox()
1016
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1017
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1018
- msgBox.setWindowTitle("Fatal Error")
1019
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1020
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1021
- msgBox.exec()
1022
-
1023
- exit(-1)
1024
-
1025
- def organism_finder(self, long_str):
1026
- try:
1027
- semi = long_str.find(";")
1028
- index = 1
1029
- while True:
1030
- if long_str[semi - index] == " ":
1031
- break
1032
- index = index + 1
1033
- return long_str[:semi - index]
1034
- except Exception as e:
1035
- logger.critical("Error trying in organism_finder() in main.")
1036
- logger.critical(e)
1037
- logger.critical(traceback.format_exc())
1038
- msgBox = QtWidgets.QMessageBox()
1039
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1040
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1041
- msgBox.setWindowTitle("Fatal Error")
1042
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1043
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1044
- msgBox.exec()
1045
-
1046
- exit(-1)
1047
-
1048
- # This method is for testing the execution of a button call to make sure the button is linked properly
1049
- def testexe(self):
1050
- try:
1051
- msgBox = QtWidgets.QMessageBox()
1052
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1053
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Question)
1054
- msgBox.setWindowTitle("Extract!")
1055
- msgBox.setText(
1056
- "Are you sure you want to quit?")
1057
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Yes)
1058
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.No)
1059
- msgBox.exec()
1060
-
1061
- if msgBox.result() == QtWidgets.QMessageBox.Yes:
1062
- # print(self.orgChoice.currentText())
1063
- sys.exit()
1064
- else:
1065
- pass
1066
- except Exception as e:
1067
- logger.critical("Error in testexe() in main.")
1068
- logger.critical(e)
1069
- logger.critical(traceback.format_exc())
1070
- msgBox = QtWidgets.QMessageBox()
1071
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1072
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1073
- msgBox.setWindowTitle("Fatal Error")
1074
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1075
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1076
- msgBox.exec()
1077
-
1078
- exit(-1)
1079
-
1080
- def getData(self):
1081
- try:
1082
- try:
1083
- self.orgChoice.currentIndexChanged.disconnect()
1084
- except Exception as e:
1085
- pass
1086
-
1087
- self.orgChoice.clear()
1088
- self.endoChoice.clear()
1089
- mypath = os.getcwd()
1090
- found = False
1091
- self.dbpath = mypath
1092
- onlyfiles = [str(f) for f in os.listdir(mypath) if os.path.isfile(os.path.join(mypath, f))]
1093
- onlyfiles.sort(key=str.lower)
1094
- self.organisms_to_files = {}
1095
- self.organisms_to_endos = {}
1096
- first = True
1097
- for file in onlyfiles:
1098
- if file.find('.cspr') != -1:
1099
- if first == True:
1100
- first = False
1101
- found = True
1102
- newname = file[0:-4]
1103
- endo = newname[newname.rfind("_")+1:-1]
1104
- hold = open(file, 'r')
1105
- buf = (hold.readline())
1106
- buf = str(buf)
1107
- buf = buf.strip()
1108
- species = buf.replace("GENOME: ",'')
1109
-
1110
- if species in self.organisms_to_files:
1111
- self.organisms_to_files[species][endo] = [file, file.replace(".cspr", "_repeats.db")]
1112
- else:
1113
- self.organisms_to_files[species] = {}
1114
- self.organisms_to_files[species][endo] = [file, file.replace(".cspr", "_repeats.db")]
1115
-
1116
- if species in self.organisms_to_endos:
1117
- self.organisms_to_endos[species].append(endo)
1118
- else:
1119
- self.organisms_to_endos[species] = [endo]
1120
- if self.orgChoice.findText(species) == -1:
1121
- self.orgChoice.addItem(species)
1122
-
1123
- #self.orgChoice.addItem("Custom Input Sequences")
1124
- # auto fill the kegg search bar with the first choice in orgChoice
1125
- if found == False:
1126
- return False
1127
-
1128
- self.endoChoice.clear()
1129
- self.endoChoice.addItems(self.organisms_to_endos[str(self.orgChoice.currentText())])
1130
- self.orgChoice.currentIndexChanged.connect(self.changeEndos)
1131
- except Exception as e:
1132
- logger.critical("Error in getData() in main.")
1133
- logger.critical(e)
1134
- logger.critical(traceback.format_exc())
1135
- msgBox = QtWidgets.QMessageBox()
1136
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1137
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1138
- msgBox.setWindowTitle("Fatal Error")
1139
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1140
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1141
- msgBox.exec()
1142
-
1143
- exit(-1)
1144
-
1145
- def changeEndos(self):
1146
- try:
1147
- if self.orgChoice.currentText() != "Custom Input Sequences":
1148
- self.Step2.setEnabled(True)
1149
- self.endoChoice.setEnabled(True)
1150
- self.radioButton_Gene.show()
1151
- self.radioButton_Position.show()
1152
- self.endoChoice.clear()
1153
- self.endoChoice.addItems(self.organisms_to_endos[str(self.orgChoice.currentText())])
1154
- else:
1155
- self.Step2.setEnabled(False)
1156
- self.endoChoice.clear()
1157
- self.endoChoice.setEnabled(False)
1158
- self.radioButton_Gene.hide()
1159
- self.radioButton_Position.hide()
1160
- except Exception as e:
1161
- logger.critical("Error in changeEndos() in main.")
1162
- logger.critical(e)
1163
- logger.critical(traceback.format_exc())
1164
- msgBox = QtWidgets.QMessageBox()
1165
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1166
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1167
- msgBox.setWindowTitle("Fatal Error")
1168
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1169
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1170
- msgBox.exec()
1171
-
1172
- exit(-1)
1173
-
1174
- def change_directory(self):
1175
- try:
1176
- mydir = QtWidgets.QFileDialog.getExistingDirectory(
1177
- None, "Open a folder...", self.dbpath, QtWidgets.QFileDialog.ShowDirsOnly)
1178
-
1179
- if not os.path.isdir(mydir):
1180
- show_message(
1181
- fontSize=12,
1182
- icon=QtWidgets.QMessageBox.Icon.Critical,
1183
- title="Not a directory",
1184
- message="The directory you selected does not exist."
1185
- )
1186
- return
1187
-
1188
- if not any(file.endswith(".cspr") for file in os.listdir(mydir)):
1189
- show_message(
1190
- fontSize=12,
1191
- icon=QtWidgets.QMessageBox.Icon.Critical,
1192
- title="Directory is invalid!",
1193
- message="You must select a directory with CSPR Files!"
1194
- )
1195
- return
1196
-
1197
- os.chdir(mydir)
1198
- mydir = mydir.replace("/", "\\") if platform.system() == "Windows" else mydir
1199
- GlobalSettings.CSPR_DB = mydir
1200
-
1201
- GlobalSettings.MTWin.directory = mydir
1202
- GlobalSettings.MTWin.get_data()
1203
- GlobalSettings.pop_Analysis.get_data()
1204
- self.getData()
1205
- self.fill_annotation_dropdown()
1206
- except Exception as e:
1207
- show_error("Error in change_directory() in main.", e)
1208
-
1209
- #change to multi-targeting window
1210
- def changeto_multitargeting(self):
1211
- try:
1212
- os.chdir(os.getcwd())
1213
- if GlobalSettings.MTWin.first_show == True:
1214
- GlobalSettings.MTWin.show()
1215
- GlobalSettings.MTWin.first_show = False
1216
- else:
1217
- GlobalSettings.MTWin.show()
1218
- GlobalSettings.mainWindow.hide()
1219
-
1220
- except Exception as e:
1221
- logger.critical("Error in changeto_multitargeting() in main.")
1222
- logger.critical(e)
1223
- logger.critical(traceback.format_exc())
1224
- msgBox = QtWidgets.QMessageBox()
1225
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1226
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1227
- msgBox.setWindowTitle("Fatal Error")
1228
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1229
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1230
- msgBox.exec()
1231
-
1232
- exit(-1)
1233
-
1234
- #change to population analysis window
1235
- def changeto_population_Analysis(self):
1236
- try:
1237
- GlobalSettings.pop_Analysis.launch()
1238
- if GlobalSettings.pop_Analysis.first_show == True:
1239
- GlobalSettings.pop_Analysis.centerUI()
1240
- GlobalSettings.pop_Analysis.first_show = False
1241
- GlobalSettings.pop_Analysis.show()
1242
- GlobalSettings.mainWindow.hide()
1243
- except Exception as e:
1244
- logger.critical("Error in changeto_population_Analysis() in main.")
1245
- logger.critical(e)
1246
- logger.critical(traceback.format_exc())
1247
- msgBox = QtWidgets.QMessageBox()
1248
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1249
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1250
- msgBox.setWindowTitle("Fatal Error")
1251
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1252
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1253
- msgBox.exec()
1254
-
1255
- exit(-1)
1256
-
1257
- def annotation_information(self):
1258
- try:
1259
- info = "Annotation files are used for searching for spacers on a gene/locus basis and can be selected here using either " \
1260
- "NCBI databases or a local file."
1261
- msgBox = QtWidgets.QMessageBox()
1262
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1263
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1264
- msgBox.setWindowTitle("Annotation Information")
1265
- msgBox.setText(
1266
- info)
1267
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
1268
- msgBox.exec()
1269
-
1270
- except Exception as e:
1271
- logger.critical("Error in annotation_information() in main.")
1272
- logger.critical(e)
1273
- logger.critical(traceback.format_exc())
1274
- msgBox = QtWidgets.QMessageBox()
1275
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1276
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1277
- msgBox.setWindowTitle("Fatal Error")
1278
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1279
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1280
- msgBox.exec()
1281
-
1282
- exit(-1)
1283
-
1284
- def open_ncbi_blast_web_page(self):
1285
- try:
1286
- webbrowser.open('https://blast.ncbi.nlm.nih.gov/Blast.cgi', new=2)
1287
- except Exception as e:
1288
- show_error("open_ncbi_blast_web_page() in main", e)
1289
-
1290
- def open_ncbi_web_page(self):
1291
- try:
1292
- webbrowser.open('https://www.ncbi.nlm.nih.gov/', new=2)
1293
- except Exception as e:
1294
- show_error("open_ncbi_web_page() in main", e)
1295
-
1296
- # def open_casper2_web_page(self):
1297
- # try:
1298
- # webbrowser.open('http://casper2.org/', new=2)
1299
- # except Exception as e:
1300
- # logger.critical("Error in open_casper2_web_page() in main.")
1301
- # logger.critical(e)
1302
- # logger.critical(traceback.format_exc())
1303
- # exit(-1)
1304
-
1305
- def visit_repo_func(self):
1306
- try:
1307
- webbrowser.open('https://github.com/TrinhLab/CASPERapp')
1308
- except Exception as e:
1309
- show_error("visit_repo_func() in main", e)
1310
-
1311
- @QtCore.pyqtSlot()
1312
- def view_results(self):
1313
- try:
1314
- #center results window on current screen
1315
- if self.Results.first_show == True:
1316
- self.Results.first_show = False
1317
- self.Results.centerUI()
1318
-
1319
- self.Results.show()
1320
- self.hide()
1321
- except Exception as e:
1322
- logger.critical("Error in view_results() in main.")
1323
- logger.critical(e)
1324
- logger.critical(traceback.format_exc())
1325
- msgBox = QtWidgets.QMessageBox()
1326
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1327
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1328
- msgBox.setWindowTitle("Fatal Error")
1329
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1330
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1331
- msgBox.exec()
1332
-
1333
- exit(-1)
1334
-
1335
- def closeFunction(self):
1336
- try:
1337
- # Attempt to close the NCBI window if it exists
1338
- try:
1339
- self.ncbi.close()
1340
- except AttributeError:
1341
- print("No NCBI window to close.")
1342
-
1343
- # Proceed with closing operations
1344
- self.myClosingWindow.get_files()
1345
- self.myClosingWindow.centerUI()
1346
- self.myClosingWindow.show()
1347
- except Exception as e:
1348
- show_error("closeFunction() in main", e)
1349
-
1350
- def close_app(self):
1351
- try:
1352
- # Attempt to close the NCBI window if it exists
1353
- try:
1354
- self.ncbi.close()
1355
- except Exception as e:
1356
- print("No NCBI window to close.")
1357
-
1358
- # Proceed with other closing operations
1359
- self.closeFunction()
1360
- self.close()
1361
- except Exception as e:
1362
- show_error("close_app() in main", e)
1363
-
1364
- #startup window class
1365
- class StartupWindow(QtWidgets.QMainWindow):
1366
- def __init__(self):
1367
- try:
1368
- super(StartupWindow, self).__init__()
1369
-
1370
- #load UX files
1371
- try:
1372
- uic.loadUi(GlobalSettings.appdir + 'startupCASPER.ui', self)
1373
- self.setWindowIcon(QtGui.QIcon(GlobalSettings.appdir + "cas9image.png"))
1374
- except Exception as e:
1375
- logger.critical("Unable to load UX files for Startup Window.")
1376
- logger.critical(e)
1377
- logger.critical(traceback.format_exc())
1378
- exit(-1)
1379
-
1380
- #set "Main" button to be the default highlighted button on startup
1381
- self.goToMain.setDefault(True)
1382
-
1383
- #get current directory, and update based on current operating system
1384
- self.currentDirectory = os.getcwd()
1385
- self.databaseDirectory = self.loadDatabaseDirectory()
1386
- GlobalSettings.CSPR_DB = self.databaseDirectory
1387
- if platform.system() == "Windows":
1388
- GlobalSettings.CSPR_DB = GlobalSettings.CSPR_DB.replace("/","\\")
1389
- else:
1390
- GlobalSettings.CSPR_DB = GlobalSettings.CSPR_DB.replace("\\","/")
1391
-
1392
- #setup event handlers for startup buttons
1393
- self.currentDirText.setText(self.databaseDirectory)
1394
- self.changeDir.clicked.connect(self.change_directory)
1395
- self.goToMain.clicked.connect(self.launchMainWindow)
1396
- self.goToNewGenome.clicked.connect(self.launchNewGenome)
1397
-
1398
- self.setWindowTitle("CASPER")
1399
- self.setWindowIcon(Qt.QIcon(GlobalSettings.appdir + "cas9image.ico"))
1400
-
1401
- #scale UI
1402
- scale_ui(self)
1403
-
1404
- except Exception as e:
1405
- logger.critical("Error initializing StartupWindow class.")
1406
- logger.critical(e)
1407
- logger.critical(traceback.format_exc())
1408
- exit(-1)
1409
-
1410
- #event handler for user clicking the "Change..." button - used for changing CASPER database directory
1411
- def change_directory(self):
1412
- try:
1413
- # Launch OS file browser
1414
- newDirectory = QtWidgets.QFileDialog.getExistingDirectory(
1415
- self, "Open a folder...", self.databaseDirectory, QtWidgets.QFileDialog.ShowDirsOnly)
1416
-
1417
- # Check if selected path is a directory in the system
1418
- if not os.path.isdir(newDirectory):
1419
- show_message(
1420
- fontSize=self.fontSize,
1421
- icon=QtWidgets.QMessageBox.Icon.Critical,
1422
- title="Not a directory",
1423
- message="The directory you selected does not exist.",
1424
- )
1425
- return
1426
-
1427
- # Ensure directory contains correct filepath format based on OS
1428
- newDirectory = newDirectory.replace("/", "\\") if platform.system() == "Windows" else newDirectory.replace("\\", "/")
1429
-
1430
- # Update text edit showing the current selected database directory
1431
- self.currentDirText.setText(newDirectory)
1432
-
1433
- # Update CASPER database directories
1434
- self.databaseDirectory = newDirectory
1435
- GlobalSettings.CSPR_DB = newDirectory
1436
-
1437
- except Exception as e:
1438
- show_error("change_directory() in startup window", e)
1439
-
1440
- #function for loading the default database directory specified in CASPERinfo
1441
- #returns: default database parsed from CASPERinfo
1442
- def loadDatabaseDirectory(self):
1443
- casperInfoPath = os.path.join(GlobalSettings.appdir, "CASPERinfo")
1444
- defaultDirectory = "Where would you like to store CASPER database files?" # Default message if directory not found
1445
-
1446
- try:
1447
- with open(casperInfoPath, 'r') as file:
1448
- for line in file:
1449
- if 'DIRECTORY:' in line:
1450
- defaultDirectory = line.strip().replace("DIRECTORY:", "").strip()
1451
- break
1452
-
1453
- # Ensure the directory path is formatted correctly based on the operating system
1454
- if platform.system() == "Windows":
1455
- defaultDirectory = defaultDirectory.replace("/", "\\")
1456
- else:
1457
- defaultDirectory = defaultDirectory.replace("\\", "/")
1458
-
1459
- logger.debug("Successfully parsed CASPERinfo for default database directory.")
1460
- return defaultDirectory
1461
-
1462
- except Exception as e:
1463
- logger.error(f"Error reading {casperInfoPath}: {e}")
1464
- logger.error(traceback.format_exc())
1465
-
1466
- return defaultDirectory
1467
-
1468
- #function for saving the currently selected database directory to CASPERinfo to be the new default value on startup
1469
- def saveDatabaseDirectory(self):
1470
- try:
1471
- #variable to hold the CASPERinfo data with new default directory change
1472
- CASPERInfoNewData = ""
1473
-
1474
- #new default directory string for CASPERinfo
1475
- newDefaultDirectory = "DIRECTORY:" + str(self.databaseDirectory)
1476
-
1477
- #open CASPERinfo file to read in the files data and add in new change
1478
- try:
1479
- CASPERInfo = open(GlobalSettings.appdir + "CASPERinfo", 'r+')
1480
- CASPERinfoData = CASPERInfo.read()
1481
- CASPERinfoData = CASPERinfoData.split('\n')
1482
- for line in CASPERinfoData:
1483
- #if directory line found, use new default directory string instead
1484
- if 'DIRECTORY:' in line:
1485
- CASPERInfoNewData = CASPERInfoNewData + "\n" + newDefaultDirectory
1486
- else:
1487
- CASPERInfoNewData = CASPERInfoNewData + "\n" + line
1488
- CASPERInfoNewData = CASPERInfoNewData[1:]
1489
-
1490
- #close CASPERinfo
1491
- CASPERInfo.close()
1492
-
1493
- #re-open the file and re-write it with current changes
1494
- CASPERInfo = open(GlobalSettings.appdir + "CASPERinfo", 'w+')
1495
- CASPERInfo.write(CASPERInfoNewData)
1496
- CASPERInfo.close()
1497
- logger.debug("Successfully updated CASPERinfo with new default database directory.")
1498
- except Exception as e:
1499
- logger.critical("Unable to write to CASPERinfo file to update database directory.")
1500
- logger.critical(e)
1501
- logger.critical(traceback.format_exc())
1502
- msgBox = QtWidgets.QMessageBox()
1503
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1504
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1505
- msgBox.setWindowTitle("Fatal Error")
1506
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1507
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1508
- msgBox.exec()
1509
-
1510
- exit(-1)
1511
- except Exception as e:
1512
- logger.critical("Error in saveDatabaseDirectory() in startup window.")
1513
- logger.critical(e)
1514
- logger.critical(traceback.format_exc())
1515
- msgBox = QtWidgets.QMessageBox()
1516
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1517
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1518
- msgBox.setWindowTitle("Fatal Error")
1519
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1520
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1521
- msgBox.exec()
1522
-
1523
- exit(-1)
1524
-
1525
- # Event handler for user clicking the "New Genome" button - used for launching New Genome
1526
- def launchNewGenome(self):
1527
- try:
1528
- # Make sure database directory variable is up-to-date based on what the user has in the text edit
1529
- self.databaseDirectory = str(self.currentDirText.text())
1530
-
1531
- if not os.path.isdir(self.databaseDirectory):
1532
- show_message(
1533
- fontSize=12,
1534
- icon=QtWidgets.QMessageBox.Icon.Critical,
1535
- title="Not a directory",
1536
- message="The directory you selected does not exist.",
1537
- )
1538
- return
1539
-
1540
- # Change directories to the specified database directory provided
1541
- os.chdir(self.databaseDirectory)
1542
-
1543
- # Write out the database directory to CASPERinfo to be the new default loaded value
1544
- self.saveDatabaseDirectory()
1545
-
1546
- # Update global database variable
1547
- GlobalSettings.CSPR_DB = self.databaseDirectory
1548
-
1549
- # Create app directories
1550
- initialize_app_directories()
1551
-
1552
- # Launch New Genome window
1553
- self.launch_new_genome()
1554
-
1555
- self.close()
1556
- except Exception as e:
1557
- show_error("launchNewGenome() in startup window", e)
1558
-
1559
- def launch_new_genome(self):
1560
- try:
1561
- GlobalSettings.mainWindow.launch_newGenome()
1562
- logger.debug("Successfully initialized New Genome in startup window.")
1563
- except Exception as e:
1564
- show_error("launch_new_genome() in startup window", e)
1565
-
1566
- # Event handler for user clicking "Main Program" button - used to launch Main Window
1567
- def launchMainWindow(self):
1568
- try:
1569
- # Make sure database directory variable is up-to-date based on what the user has in the text edit
1570
- self.databaseDirectory = str(self.currentDirText.text())
1571
-
1572
- # Make sure the path is a valid path before launching New Genome
1573
- if not os.path.isdir(self.databaseDirectory):
1574
- show_message(
1575
- fontSize=12,
1576
- icon=QtWidgets.QMessageBox.Icon.Critical,
1577
- title="Not a directory",
1578
- message="The directory you selected does not exist.",
1579
- )
1580
- return
1581
-
1582
- # Check if database directory has CSPR files in it
1583
- if not any(file.endswith(".cspr") for file in os.listdir(self.databaseDirectory)):
1584
- show_message(
1585
- fontSize=12,
1586
- icon=QtWidgets.QMessageBox.Icon.Critical,
1587
- title="Directory is invalid!",
1588
- message="You must select a directory with CSPR Files!",
1589
- )
1590
- return
1591
-
1592
- # Change directory to database directory
1593
- os.chdir(self.databaseDirectory)
1594
-
1595
- # Update database directory global variable
1596
- GlobalSettings.CSPR_DB = self.databaseDirectory
1597
-
1598
- # Save database directory to CASPERinfo
1599
- self.saveDatabaseDirectory()
1600
-
1601
- initialize_app_directories()
1602
-
1603
- # Fill in organism/endo/annotation dropdown information for main, mulit-targeting, and populatin analysis
1604
- self.load_dropdown_data()
1605
-
1606
- # Show main window
1607
- if GlobalSettings.mainWindow.first_show:
1608
- GlobalSettings.mainWindow.first_show = False
1609
- GlobalSettings.mainWindow.show()
1610
- self.close()
1611
-
1612
- except Exception as e:
1613
- show_error("launchMainWindow() in startup window", e)
1614
-
1615
- def load_dropdown_data(self):
1616
- try:
1617
- GlobalSettings.mainWindow.getData()
1618
- GlobalSettings.mainWindow.fill_annotation_dropdown()
1619
- logger.debug("Successfully loaded organism/endo/annotation drop down information in Main.")
1620
- except Exception as e:
1621
- show_error("load_dropdown_data() in Main", e)
1622
-
1623
- try:
1624
- GlobalSettings.MTWin.launch()
1625
- logger.debug("Successfully loaded organism/endo drop down information in Multi-targeting.")
1626
- except Exception as e:
1627
- show_error("load_dropdown_data() in Multi-targeting", e)
1628
-
1629
- try:
1630
- GlobalSettings.pop_Analysis.launch()
1631
- logger.debug("Successfully loaded organism/endo drop down information in Population Analysis.")
1632
- except Exception as e:
1633
- show_error("load_dropdown_data() in Population Analysis", e)
1634
-
1635
- def initialize_app_directories():
1636
- required_dirs = ["FNA", "GBFF"]
1637
- for directory in required_dirs:
1638
- path = os.path.join(GlobalSettings.CSPR_DB, directory)
1639
- if not os.path.exists(path):
1640
- os.makedirs(path, exist_ok=True)
1641
- logging.info(f"Directory created: {path}")
1642
-
1643
  def setup_logger():
1644
  logger.info(f"System OS: {platform.system()}")
1645
 
1646
  if hasattr(sys, 'frozen'):
1647
- #log CASPER is in packaged format
1648
  logger.info("Running a packaged version of CASPER.")
1649
  GlobalSettings.appdir = sys.executable
1650
  if platform.system() == 'Windows':
@@ -1653,11 +27,10 @@ def setup_logger():
1653
  GlobalSettings.appdir = GlobalSettings.appdir[:GlobalSettings.appdir.rfind("Contents/") + 9] + "Resources/"
1654
 
1655
  else:
1656
- # log CASPER is not in packaged format
1657
  logger.info("Running a non-packaged version of CASPER.")
1658
  GlobalSettings.appdir = os.path.dirname(os.path.abspath(__file__)) + ('\\' if platform.system() == 'Windows' else '/')
1659
 
1660
- fh = logging.FileHandler(GlobalSettings.appdir + 'logs/CASPER.log', mode='w')
1661
  fh_formatter = logging.Formatter('%(asctime)s %(levelname)s %(lineno)d:%(filename)s(%(process)d) - %(message)s')
1662
  fh.setFormatter(fh_formatter)
1663
  fh.setLevel(logging.DEBUG)
 
 
1
  import sys
2
  import os
 
3
  from PyQt5 import QtWidgets, Qt, QtGui, QtCore, uic
4
+ import models.GlobalSettings as GlobalSettings
5
+ import controllers.multitargeting as multitargeting
6
+ import controllers.populationAnalysis as populationAnalysis
 
 
 
 
 
 
 
 
 
 
 
 
7
  import platform
 
 
 
 
8
  import logging
9
+ from utils.ui import show_error, center_ui
10
+ from views.annotation_functions import *
11
+ from views.StartupWindow import StartupWindow
12
+ from views.CMainWindow import CMainWindow
13
 
 
14
  logger = GlobalSettings.logger
15
 
16
  fontSize = 12
17
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  def setup_logger():
19
  logger.info(f"System OS: {platform.system()}")
20
 
21
  if hasattr(sys, 'frozen'):
 
22
  logger.info("Running a packaged version of CASPER.")
23
  GlobalSettings.appdir = sys.executable
24
  if platform.system() == 'Windows':
 
27
  GlobalSettings.appdir = GlobalSettings.appdir[:GlobalSettings.appdir.rfind("Contents/") + 9] + "Resources/"
28
 
29
  else:
 
30
  logger.info("Running a non-packaged version of CASPER.")
31
  GlobalSettings.appdir = os.path.dirname(os.path.abspath(__file__)) + ('\\' if platform.system() == 'Windows' else '/')
32
 
33
+ fh = logging.FileHandler(GlobalSettings.appdir + 'logs/app.log', mode='w')
34
  fh_formatter = logging.Formatter('%(asctime)s %(levelname)s %(lineno)d:%(filename)s(%(process)d) - %(message)s')
35
  fh.setFormatter(fh_formatter)
36
  fh.setLevel(logging.DEBUG)
CSPRparser.py → models/CSPRparser.py RENAMED
@@ -1,18 +1,14 @@
1
- from Algorithms import SeqTranslate
2
  import gzip
3
 
4
  ##################################################################################################################################
5
- # CLASS NAME: CSPRparser
6
  # Use: Use as a parser for the cspr files
7
  # Precondition: Only to the used with .cspr files. Will not work with any other files
8
  # This class also took some of the parsing functions from with classes (Multitargeting and Results) and stores them in here
9
  ##################################################################################################################################
10
 
11
  class CSPRparser:
12
-
13
  def __init__(self, inputFileName):
14
-
15
- # variables used in this class
16
  self.multiSum = 0 # multitargetting sum taken from the previous version of make_graphs
17
  self.multiCount = 0 # multitargetting count taken from the previous version of make_graphs
18
  self.seqTrans = SeqTranslate() # SeqTranslate variable. for decrompressing the data
 
1
+ from utils.Algorithms import SeqTranslate
2
  import gzip
3
 
4
  ##################################################################################################################################
 
5
  # Use: Use as a parser for the cspr files
6
  # Precondition: Only to the used with .cspr files. Will not work with any other files
7
  # This class also took some of the parsing functions from with classes (Multitargeting and Results) and stores them in here
8
  ##################################################################################################################################
9
 
10
  class CSPRparser:
 
11
  def __init__(self, inputFileName):
 
 
12
  self.multiSum = 0 # multitargetting sum taken from the previous version of make_graphs
13
  self.multiCount = 0 # multitargetting count taken from the previous version of make_graphs
14
  self.seqTrans = SeqTranslate() # SeqTranslate variable. for decrompressing the data
GlobalSettings.py → models/GlobalSettings.py RENAMED
File without changes
ncbi.py DELETED
@@ -1,1686 +0,0 @@
1
- from Bio import Entrez
2
- from bs4 import BeautifulSoup
3
- from PyQt5 import QtWidgets, Qt, QtCore, uic
4
- from ftplib import FTP
5
- import gzip
6
- import pandas as pd
7
- import shutil
8
- import os, time
9
- import ssl
10
- import GlobalSettings
11
- import platform
12
- import traceback
13
- import math
14
-
15
- #global logger
16
- logger = GlobalSettings.logger
17
-
18
- ssl._create_default_https_context = ssl._create_unverified_context
19
-
20
- Entrez.email = "casper2informatics@gmail.com"
21
-
22
- #model for filtering columns in ncbi table
23
- class CustomProxyModel(QtCore.QSortFilterProxyModel):
24
-
25
- def __init__(self, parent=None):
26
- try:
27
- super().__init__(parent)
28
- self._filters = dict()
29
- except Exception as e:
30
- logger.critical("Error initializing CustomProxyModel class in ncbi tool.")
31
- logger.critical(e)
32
- logger.critical(traceback.format_exc())
33
- msgBox = QtWidgets.QMessageBox()
34
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
35
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
36
- msgBox.setWindowTitle("Fatal Error")
37
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
38
- msgBox.addButton(QtWidge/ts.QMessageBox.StandardButton.Close)
39
- msgBox.exec()
40
-
41
-
42
- exit(-1)
43
-
44
- @property
45
- def filters(self):
46
- try:
47
- return self._filters
48
- except Exception as e:
49
- logger.critical("Error in filter() in custom proxy model in ncbi tool.")
50
- logger.critical(e)
51
- logger.critical(traceback.format_exc())
52
- msgBox = QtWidgets.QMessageBox()
53
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
54
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
55
- msgBox.setWindowTitle("Fatal Error")
56
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
57
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
58
- msgBox.exec()
59
-
60
-
61
- exit(-1)
62
-
63
- def setFilter(self, expresion, column):
64
- try:
65
- if expresion:
66
- self.filters[column] = expresion
67
- elif column in self.filters:
68
- del self.filters[column]
69
- self.invalidateFilter()
70
- except Exception as e:
71
- logger.critical("Error in setFilters() in custom proxy model in ncbi tool.")
72
- logger.critical(e)
73
- logger.critical(traceback.format_exc())
74
- msgBox = QtWidgets.QMessageBox()
75
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
76
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
77
- msgBox.setWindowTitle("Fatal Error")
78
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
79
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
80
- msgBox.exec()
81
-
82
-
83
- exit(-1)
84
-
85
- def filterAcceptsRow(self, source_row, source_parent):
86
- try:
87
- for column, expresion in self.filters.items():
88
- text = self.sourceModel().index(source_row, column, source_parent).data()
89
- regex = QtCore.QRegExp(
90
- expresion, QtCore.Qt.CaseInsensitive, QtCore.QRegExp.RegExp
91
- )
92
- if regex.indexIn(text) == -1:
93
- return False
94
- return True
95
-
96
- except Exception as e:
97
- logger.critical("Error in filterAcceptsRow() in custom proxy model in ncbi tool.")
98
- logger.critical(e)
99
- logger.critical(traceback.format_exc())
100
- msgBox = QtWidgets.QMessageBox()
101
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
102
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
103
- msgBox.setWindowTitle("Fatal Error")
104
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
105
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
106
- msgBox.exec()
107
-
108
-
109
- exit(-1)
110
-
111
-
112
- #model for the data in the ncbi search table
113
- class PandasModel(QtCore.QAbstractTableModel):
114
-
115
- def __init__(self, df=pd.DataFrame(), parent=None):
116
- try:
117
- QtCore.QAbstractTableModel.__init__(self, parent=parent)
118
- self._df = df.copy()
119
- except Exception as e:
120
- logger.critical("Error initializing PandasModel class in ncbi tool.")
121
- logger.critical(e)
122
- logger.critical(traceback.format_exc())
123
- msgBox = QtWidgets.QMessageBox()
124
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
125
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
126
- msgBox.setWindowTitle("Fatal Error")
127
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
128
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
129
- msgBox.exec()
130
-
131
-
132
- exit(-1)
133
-
134
- def toDataFrame(self):
135
- try:
136
- return self._df.copy()
137
-
138
- except Exception as e:
139
- logger.critical("Error in toDataFrame() in Pandas Model in ncbi tool.")
140
- logger.critical(e)
141
- logger.critical(traceback.format_exc())
142
- msgBox = QtWidgets.QMessageBox()
143
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
144
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
145
- msgBox.setWindowTitle("Fatal Error")
146
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
147
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
148
- msgBox.exec()
149
-
150
-
151
- exit(-1)
152
-
153
- def headerData(self, section, orientation, role=QtCore.Qt.DisplayRole):
154
- try:
155
- if role != QtCore.Qt.DisplayRole:
156
- return QtCore.QVariant()
157
-
158
- if orientation == QtCore.Qt.Horizontal:
159
- try:
160
- return self._df.columns.tolist()[section]
161
- except (IndexError, ):
162
- return QtCore.QVariant()
163
- elif orientation == QtCore.Qt.Vertical:
164
- try:
165
- return self._df.index.tolist()[section]
166
- except (IndexError, ):
167
- return QtCore.QVariant()
168
- except Exception as e:
169
- logger.critical("Error in headerData() in Pandas Model in ncbi tool.")
170
- logger.critical(e)
171
- logger.critical(traceback.format_exc())
172
- msgBox = QtWidgets.QMessageBox()
173
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
174
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
175
- msgBox.setWindowTitle("Fatal Error")
176
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
177
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
178
- msgBox.exec()
179
-
180
-
181
- exit(-1)
182
-
183
- def data(self, index, role=QtCore.Qt.DisplayRole):
184
- try:
185
- if role == QtCore.Qt.TextAlignmentRole:
186
- return QtCore.Qt.AlignCenter
187
- if role != QtCore.Qt.DisplayRole:
188
- return QtCore.QVariant()
189
- if not index.isValid():
190
- return QtCore.QVariant()
191
-
192
- return QtCore.QVariant(str(self._df.iloc[index.row(), index.column()]))
193
-
194
- except Exception as e:
195
- logger.critical("Error in data() in Pandas Model in ncbi tool.")
196
- logger.critical(e)
197
- logger.critical(traceback.format_exc())
198
- msgBox = QtWidgets.QMessageBox()
199
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
200
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
201
- msgBox.setWindowTitle("Fatal Error")
202
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
203
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
204
- msgBox.exec()
205
-
206
-
207
- exit(-1)
208
-
209
- def setData(self, index, value, role):
210
- try:
211
- row = self._df.index[index.row()]
212
- col = self._df.columns[index.column()]
213
- if hasattr(value, 'toPyObject'):
214
- # PyQt4 gets a QVariant
215
- value = value.toPyObject()
216
- else:
217
- # PySide gets an unicode
218
- dtype = self._df[col].dtype
219
- if dtype != object:
220
- value = None if value == '' else dtype.type(value)
221
- self._df.set_value(row, col, value)
222
- return True
223
- except Exception as e:
224
- logger.critical("Error in setData() in Pandas Model in ncbi tool.")
225
- logger.critical(e)
226
- logger.critical(traceback.format_exc())
227
- msgBox = QtWidgets.QMessageBox()
228
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
229
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
230
- msgBox.setWindowTitle("Fatal Error")
231
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
232
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
233
- msgBox.exec()
234
-
235
-
236
- exit(-1)
237
-
238
- def rowCount(self, parent=QtCore.QModelIndex()):
239
- try:
240
- return len(self._df.index)
241
-
242
- except Exception as e:
243
- logger.critical("Error in rowCount() in Pandas Model in ncbi tool.")
244
- logger.critical(e)
245
- logger.critical(traceback.format_exc())
246
- msgBox = QtWidgets.QMessageBox()
247
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
248
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
249
- msgBox.setWindowTitle("Fatal Error")
250
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
251
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
252
- msgBox.exec()
253
-
254
-
255
- exit(-1)
256
-
257
- def columnCount(self, parent=QtCore.QModelIndex()):
258
- try:
259
- return len(self._df.columns)
260
- except Exception as e:
261
- logger.critical("Error in columnCount() in Pandas Model in ncbi tool.")
262
- logger.critical(e)
263
- logger.critical(traceback.format_exc())
264
- msgBox = QtWidgets.QMessageBox()
265
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
266
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
267
- msgBox.setWindowTitle("Fatal Error")
268
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
269
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
270
- msgBox.exec()
271
-
272
-
273
- exit(-1)
274
-
275
- def sort(self, column, order):
276
- try:
277
- colname = self._df.columns.tolist()[column]
278
- self.layoutAboutToBeChanged.emit()
279
- self._df.sort_values(colname, ascending=order, inplace=True)
280
- self._df.reset_index(inplace=True, drop=True)
281
- self.layoutChanged.emit()
282
- except Exception as e:
283
- logger.critical("Error in sort() in Pandas Model in ncbi tool.")
284
- logger.critical(e)
285
- logger.critical(traceback.format_exc())
286
- msgBox = QtWidgets.QMessageBox()
287
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
288
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
289
- msgBox.setWindowTitle("Fatal Error")
290
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
291
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
292
- msgBox.exec()
293
-
294
-
295
- exit(-1)
296
-
297
- ## Taken from StackOverflow: https://stackoverflow.com/questions/57607072/update-pyqt-progress-from-another-thread-running-ftp-download
298
- class DownloadThread(QtCore.QThread):
299
- """ Overall signals """
300
- finished = QtCore.pyqtSignal(object)
301
- started = QtCore.pyqtSignal(object)
302
-
303
- """ Download specific signals """
304
- data_progress = QtCore.pyqtSignal(object) # This signal emits progress data for the progress bar
305
- data_size = QtCore.pyqtSignal(object) # This signal emits the size of the file being downloaded
306
- file_started = QtCore.pyqtSignal(object) # This signal emits when the file starts downloading
307
- file_finished = QtCore.pyqtSignal(object) # This signal emits the name of the file downloaded
308
-
309
- def __init__(self,parent,url,id):
310
- try:
311
- QtCore.QThread.__init__(self,parent)
312
- self.id = id # Initialize ID
313
- self.url = url # Initialize URL
314
- self.ftp = FTP('ftp.ncbi.nlm.nih.gov') # Initialize FTP object
315
- self.ftp.login()
316
- except Exception as e:
317
- logger.critical("Error initializing Thread class in ncbi tool.")
318
- logger.critical(e)
319
- logger.critical(traceback.format_exc())
320
- msgBox = QtWidgets.QMessageBox()
321
- msgBox.setStyleSheet("font: " + str(GlobalSettings.mainWindow.ncbi.fontSize) + "pt 'Arial'")
322
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
323
- msgBox.setWindowTitle("Fatal Error")
324
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
325
- msgBox.addButton(QtWidge/ts.QMessageBox.StandardButton.Close)
326
- msgBox.exec()
327
- exit(-1)
328
-
329
- def run(self):
330
- try:
331
- ### Start by making sure the url is valid
332
- if self.url == "":
333
- self.finished.emit((self.id, False))
334
- return
335
- else:
336
- self.started.emit(self.id)
337
- self.ftp.cwd(self.url) # Change to appropriate directory
338
- dir_files = self.ftp.nlst() # Get list of files in directory
339
- for file in dir_files: # Loop through every file in the directory
340
- if GlobalSettings.mainWindow.ncbi.gbff_checkbox.isChecked(): # If a GBFF is supposed to be downloaded
341
- if file.find('genomic.gbff') != -1: # If a GBFF exists in this directory
342
-
343
- # check OS for output path
344
- if platform.system() == "Windows":
345
- output_file = GlobalSettings.CSPR_DB + "\\GBFF\\" + file
346
- else:
347
- output_file = GlobalSettings.CSPR_DB + "/GBFF/" + file
348
-
349
- self.ftp.voidcmd('TYPE I')
350
- totalsize = self.ftp.size(file) # Get size of file that is being downloaded
351
- # The first signal sets the maximum for the progress bar
352
- self.file_started.emit((self.id,'Downloading GBFF: ' + str(round(totalsize/1e6,2)) + 'MB...',str(totalsize))) # Emit that the file download is starting and size of file
353
- with open(output_file, 'wb') as self.f:
354
- self.ftp.retrbinary(f"RETR {file}", self.file_write) # Download the file, emitting progress as we go
355
- self.decompress_file(output_file) # Decompress the file
356
- self.file_finished.emit((self.id,'GBFF Downloaded!',output_file)) # Once download is finished, emit signal
357
-
358
- if GlobalSettings.mainWindow.ncbi.fna_checkbox.isChecked(): # If a FNA is supposed to be downloaded
359
- if file.find('genomic.fna') != -1 and file.find('_cds_') == -1 and file.find('_rna_') == -1: # If a FNA exists in this directory
360
-
361
- # check OS for output path
362
- if platform.system() == "Windows":
363
- output_file = GlobalSettings.CSPR_DB + "\\FNA\\" + file
364
- else:
365
- output_file = GlobalSettings.CSPR_DB + "/FNA/" + file
366
-
367
- self.ftp.voidcmd('TYPE I')
368
- totalsize = self.ftp.size(file) # Get size of file that is being downloaded
369
- self.file_started.emit((self.id,'Downloading FNA: ' + str(round(totalsize/1e6,2)) + 'MB...',str(totalsize))) # Emit that file download is starting
370
- with open(output_file, 'wb') as self.f:
371
- self.ftp.retrbinary(f"RETR {file}", self.file_write) # Download the file
372
-
373
- self.decompress_file(output_file) # Decompress the file
374
- self.file_finished.emit((self.id,'FNA Downloaded!',output_file)) # Once download is finished, emit signal
375
- self.finished.emit((self.id,True))
376
- self.ftp.quit() # Stop the FTP connection once everything has been downloaded
377
- except Exception as e:
378
- logger.critical("Error downloading file within DownloadThread class.")
379
- logger.critical(e)
380
- logger.critical(traceback.format_exc())
381
- msgBox = QtWidgets.QMessageBox()
382
- msgBox.setStyleSheet("font: " + str(GlobalSettings.mainWindow.ncbi.fontSize) + "pt 'Arial'")
383
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
384
- msgBox.setWindowTitle("Fatal Error")
385
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
386
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
387
- msgBox.exec()
388
- exit(-1)
389
-
390
- def file_write(self, data):
391
- self.f.write(data) # Write the downloaded data to a file
392
- # The other signals increase a progress
393
- self.data_progress.emit((self.id,str(len(data)))) # Emit a signal updating progress
394
-
395
- # decompress file function
396
- def decompress_file(self, filename):
397
- try:
398
- block_size = 65536
399
- with gzip.open(filename, 'rb') as f_in:
400
- with open(str(filename).replace('.gz', ''), 'wb') as f_out:
401
- while True:
402
- block = f_in.read(block_size)
403
- if not block:
404
- break
405
- else:
406
- f_out.write(block)
407
- os.remove(str(filename))
408
- except Exception as e:
409
- logger.critical("Error in decompress_file() in ncbi tool.")
410
- logger.critical(e)
411
- logger.critical(traceback.format_exc())
412
- msgBox = QtWidgets.QMessageBox()
413
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
414
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
415
- msgBox.setWindowTitle("Fatal Error")
416
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
417
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
418
- msgBox.exec()
419
- exit(-1)
420
-
421
- #ncbi
422
- class NCBI_search_tool(QtWidgets.QMainWindow):
423
-
424
- def __init__(self):
425
- try:
426
- super(NCBI_search_tool, self).__init__()
427
- uic.loadUi(GlobalSettings.appdir + 'ncbi.ui', self)
428
- self.setWindowIcon(Qt.QIcon(GlobalSettings.appdir + "cas9image.ico"))
429
- self.setWindowTitle("NCBI Download Tool")
430
- self.logicalIndex = 0
431
- self.filters = dict()
432
- self.download_button.clicked.connect(self.download_files_wrapper)
433
- self.search_button.clicked.connect(self.query_db)
434
- self.ncbi_table.verticalHeader().hide()
435
- self.all_rows.clicked.connect(self.select_all)
436
- self.back_button.clicked.connect(self.go_back)
437
- self.ncbi_table.setFocusPolicy(QtCore.Qt.NoFocus)
438
- self.progressBar.setValue(0)
439
- self.rename_window = rename_window()
440
- self.rename_window.submit_button.clicked.connect(self.submit_rename)
441
- self.rename_window.go_back.clicked.connect(self.rename_go_back)
442
- self.df = pd.DataFrame()
443
- groupbox_style = """
444
- QGroupBox:title{subcontrol-origin: margin;
445
- left: 10px;
446
- padding: 0 5px 0 5px;}
447
- QGroupBox#Step1{border: 2px solid rgb(111,181,110);
448
- border-radius: 9px;
449
- font: bold 14pt 'Arial';
450
- margin-top: 10px;}"""
451
- self.Step1.setStyleSheet(groupbox_style)
452
- self.Step2.setStyleSheet(groupbox_style.replace("Step1","Step2"))
453
- self.Step3.setStyleSheet(groupbox_style.replace("Step1","Step3"))
454
- self.ncbi_table.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
455
- self.ncbi_table.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
456
- #navigation page
457
- self.goToPrompt = goToPrompt()
458
- self.goToPrompt.stay.clicked.connect(self.stay)
459
- self.goToPrompt.close.clicked.connect(self.close)
460
-
461
- #loading label
462
- self.loading_window = loading_window()
463
-
464
-
465
- self.genbank_checkbox.toggled.connect(self.check_genbank)
466
-
467
- #scale UI
468
- self.first_show = True
469
- self.scaleUI()
470
-
471
- except Exception as e:
472
- logger.critical("Error initializing NCBI_search_tool class.")
473
- logger.critical(e)
474
- logger.critical(traceback.format_exc())
475
- msgBox = QtWidgets.QMessageBox()
476
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
477
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
478
- msgBox.setWindowTitle("Fatal Error")
479
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
480
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
481
- msgBox.exec()
482
- exit(-1)
483
-
484
- def check_genbank(self):
485
- if self.genbank_checkbox.isChecked():
486
- msgBox = QtWidgets.QMessageBox()
487
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
488
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Warning)
489
- msgBox.setWindowTitle("Warning!")
490
- msgBox.setText("Warning!\n\nThe GenBank collection may contain poorly or partially annotated annotation files. We highly recommend using the RefSeq collection if it is available.")
491
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
492
- msgBox.exec()
493
- else:
494
- pass # Do nothing
495
-
496
- #scale UI based on current screen
497
- def scaleUI(self):
498
- try:
499
- self.repaint()
500
- QtWidgets.QApplication.processEvents()
501
-
502
- screen = self.screen()
503
- dpi = screen.physicalDotsPerInch()
504
- width = screen.geometry().width()
505
- height = screen.geometry().height()
506
-
507
- # font scaling
508
- fontSize = 12
509
- self.fontSize = fontSize
510
- self.centralWidget().setStyleSheet("font: " + str(fontSize) + "pt 'Arial';")
511
-
512
- # CASPER header scaling
513
- fontSize = 30
514
- self.title.setStyleSheet("font: bold " + str(fontSize) + "pt 'Arial';")
515
-
516
- self.adjustSize()
517
-
518
- currentWidth = self.size().width()
519
- currentHeight = self.size().height()
520
-
521
- # window scaling
522
- # 1920x1080 => 850x750
523
- scaledWidth = int((width * 1000) / 1920)
524
- scaledHeight = int((height * 750) / 1080)
525
-
526
- if scaledHeight < currentHeight:
527
- scaledHeight = currentHeight
528
- if scaledWidth < currentWidth:
529
- scaledWidth = currentWidth
530
-
531
- screen = QtWidgets.QApplication.desktop().screenNumber(QtWidgets.QApplication.desktop().cursor().pos())
532
- centerPoint = QtWidgets.QApplication.desktop().screenGeometry(screen).center()
533
- x = centerPoint.x()
534
- y = centerPoint.y()
535
- x = x - (math.ceil(scaledWidth / 2))
536
- y = y - (math.ceil(scaledHeight / 2))
537
- self.setGeometry(x, y, scaledWidth, scaledHeight)
538
-
539
- self.repaint()
540
- QtWidgets.QApplication.processEvents()
541
-
542
- except Exception as e:
543
- logger.critical("Error in scaleUI() in NCBI tool.")
544
- logger.critical(e)
545
- logger.critical(traceback.format_exc())
546
- msgBox = QtWidgets.QMessageBox()
547
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
548
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
549
- msgBox.setWindowTitle("Fatal Error")
550
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
551
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
552
- msgBox.exec()
553
-
554
-
555
- exit(-1)
556
-
557
- #center UI on current screen
558
- def centerUI(self):
559
- try:
560
- self.repaint()
561
- QtWidgets.QApplication.processEvents()
562
-
563
- # center window on current screen
564
- width = self.width()
565
- height = self.height()
566
- screen = QtWidgets.QApplication.desktop().screenNumber(QtWidgets.QApplication.desktop().cursor().pos())
567
- centerPoint = QtWidgets.QApplication.desktop().screenGeometry(screen).center()
568
- x = centerPoint.x()
569
- y = centerPoint.y()
570
- x = x - (math.ceil(width / 2))
571
- y = y - (math.ceil(height / 2))
572
- self.setGeometry(x, y, width, height)
573
-
574
- self.repaint()
575
- QtWidgets.QApplication.processEvents()
576
- except Exception as e:
577
- logger.critical("Error in centerUI() in NCBI tool.")
578
- logger.critical(e)
579
- logger.critical(traceback.format_exc())
580
- msgBox = QtWidgets.QMessageBox()
581
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
582
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
583
- msgBox.setWindowTitle("Fatal Error")
584
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
585
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
586
- msgBox.exec()
587
-
588
-
589
- exit(-1)
590
-
591
- def go_back(self):
592
- try:
593
- """ Clear table """
594
- self.df = pd.DataFrame() ###Make empty DF
595
- self.model = PandasModel(self.df)
596
- self.proxy = CustomProxyModel(self)
597
- self.proxy.setSourceModel(self.model)
598
- self.ncbi_table.setModel(self.proxy)
599
- self.ncbi_table.verticalHeader().hide()
600
- """ Clear all line edits """
601
- self.organism_line_edit.clear()
602
- self.infra_name_line_edit.clear()
603
- self.ret_max_line_edit.setText("100")
604
- self.infra_name_line_edit.clear()
605
- """ Reset all checkboxes """
606
- self.yes_box.setChecked(False)
607
- self.genbank_checkbox.setChecked(False)
608
- self.refseq_checkbox.setChecked(False)
609
- self.gbff_checkbox.setChecked(False)
610
- self.fna_checkbox.setChecked(False)
611
- """ Hide window """
612
- self.close()
613
- except Exception as e:
614
- logger.critical("Error in go_back() in ncbi tool.")
615
- logger.critical(e)
616
- logger.critical(traceback.format_exc())
617
- msgBox = QtWidgets.QMessageBox()
618
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
619
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
620
- msgBox.setWindowTitle("Fatal Error")
621
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
622
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
623
- msgBox.exec()
624
-
625
-
626
- exit(-1)
627
-
628
- @QtCore.pyqtSlot()
629
- def query_db(self):
630
- try:
631
- #show loading
632
- self.loading_window.loading_bar.setValue(5)
633
- self.loading_window.centerUI()
634
- self.loading_window.show()
635
- QtCore.QCoreApplication.processEvents()
636
-
637
- #setup table
638
- self.comboBox = QtWidgets.QComboBox(self)
639
- self.horizontalHeader = self.ncbi_table.horizontalHeader()
640
- self.horizontalHeader.sectionClicked.connect(self.on_view_horizontalHeader_sectionClicked)
641
-
642
- #Build Query commands
643
- retmax = int(self.ret_max_line_edit.text())
644
- if retmax == "":
645
- retmax = 100
646
- org = self.organism_line_edit.text()
647
- term = '"' + org + '"[Organism]'
648
- if self.yes_box.isChecked():
649
- term += ' AND "Complete Genome"[Assembly Level]'
650
- if self.infra_name_line_edit.text() != "":
651
- term += ' AND "' + self.infra_name_line_edit.text() + '"[Infraspecific name]'
652
-
653
- #Search DB for IDs
654
- handle = Entrez.esearch(db="assembly", retmax=retmax, term=term)
655
- content = handle.readlines()
656
- content = "".join(str(content))
657
- #bs_content = BeautifulSoup(content, "lxml")
658
- bs_content = BeautifulSoup(content, "html.parser")
659
-
660
- self.loading_window.loading_bar.setValue(20)
661
- QtCore.QCoreApplication.processEvents()
662
-
663
- #Extract IDs
664
- idlist = bs_content.find('idlist')
665
- ids = idlist.find_all('id')
666
- ids = [i.text for i in ids]
667
-
668
- self.loading_window.loading_bar.setValue(35)
669
- QtCore.QCoreApplication.processEvents()
670
-
671
- # Get Details on IDs
672
- handle = Entrez.esummary(db="assembly", id=','.join(ids))
673
- content = handle.readlines()
674
- handle.close()
675
- content = "".join(str(content))
676
- #bs_content = BeautifulSoup(content, 'lxml')
677
- bs_content = BeautifulSoup(content, "html.parser")
678
-
679
- self.loading_window.loading_bar.setValue(55)
680
-
681
- QtCore.QCoreApplication.processEvents()
682
-
683
- #Prep Data for Table
684
- assembly_name = bs_content.find_all('assemblyname')
685
- genbank_ids = bs_content.find_all('genbank')
686
- refseq_ids = bs_content.find_all('refseq')
687
- assembly_status = bs_content.find_all('assemblystatus')
688
- species_name = bs_content.find_all('speciesname')
689
- temp_strains = bs_content.find_all('infraspecieslist')
690
- assembly_name = [i.text for i in assembly_name]
691
- genbank_ids = [i.text for i in genbank_ids]
692
- refseq_ids = [i.text for i in refseq_ids]
693
- assembly_status = [i.text for i in assembly_status]
694
- species_name = [i.text for i in species_name]
695
- ids = [int(i) for i in ids]
696
- strains = []
697
- for i in range(len(genbank_ids)):
698
- temp_str = str(temp_strains[i])
699
- #temp_str = BeautifulSoup(temp_str, 'lxml')
700
- temp_str = BeautifulSoup(temp_str, 'html.parser')
701
- temp_str = temp_str.find('sub_value')
702
- if temp_str != None:
703
- strains.append(temp_str.text)
704
- else:
705
- strains.append('N/A')
706
-
707
- self.loading_window.loading_bar.setValue(65)
708
- QtCore.QCoreApplication.processEvents()
709
-
710
- #Get ftp links
711
- genbank_links = bs_content.find_all('ftppath_genbank')
712
- refseq_links = bs_content.find_all('ftppath_refseq')
713
- refseq_links = bs_content.find_all('ftppath_refseq')
714
- genbank_links = [i.text for i in genbank_links]
715
- refseq_links = [i.text for i in refseq_links]
716
- self.genbank_ftp_dict = {}
717
- self.refseq_ftp_dict = {}
718
- for i in range(len(ids)):
719
- if genbank_ids[i] == '':
720
- self.genbank_ftp_dict[ids[i]] = ''
721
- else:
722
- self.genbank_ftp_dict[ids[i]] = genbank_links[i] + '/'
723
- if refseq_ids[i] == '':
724
- self.refseq_ftp_dict[ids[i]] = ''
725
- else:
726
- self.refseq_ftp_dict[ids[i]] = refseq_links[i] + '/'
727
-
728
- self.loading_window.loading_bar.setValue(80)
729
- QtCore.QCoreApplication.processEvents()
730
-
731
- #Build dataframe
732
- self.df = pd.DataFrame({'ID': ids,
733
- 'Species Name' : species_name,
734
- 'Strain' : strains,
735
- 'Assembly Name' : assembly_name,
736
- 'GenBank assembly accession': genbank_ids,
737
- 'RefSeq assembly accession': refseq_ids,
738
- 'Assembly Status': assembly_status})
739
-
740
- self.loading_window.loading_bar.setValue(90)
741
- QtCore.QCoreApplication.processEvents()
742
-
743
- #Build table view
744
- self.df.replace('', 'N/A', inplace=True)
745
- self.model = PandasModel(self.df)
746
- self.proxy = CustomProxyModel(self)
747
- self.proxy.setSourceModel(self.model)
748
- self.ncbi_table.setModel(self.proxy)
749
- self.ncbi_table.resizeColumnsToContents()
750
- self.comboBox.addItems(["{0}".format(col) for col in self.model._df.columns])
751
- self.activateWindow()
752
-
753
- #close loading gif
754
- self.loading_window.hide()
755
- self.loading_window.loading_bar.setValue(0)
756
- QtCore.QCoreApplication.processEvents()
757
- except Exception as e:
758
- logger.critical("Error in query_db() in ncbi tool.")
759
- logger.critical(e)
760
- logger.critical(traceback.format_exc())
761
- msgBox = QtWidgets.QMessageBox()
762
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
763
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
764
- msgBox.setWindowTitle("Fatal Error")
765
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
766
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
767
- msgBox.exec()
768
-
769
-
770
- exit(-1)
771
-
772
- @QtCore.pyqtSlot(int)
773
- def on_view_horizontalHeader_sectionClicked(self, logicalIndex):
774
- try:
775
- self.logicalIndex = logicalIndex
776
- self.menuValues = QtWidgets.QMenu(self)
777
- self.signalMapper = QtCore.QSignalMapper(self)
778
- self.comboBox.blockSignals(True)
779
- self.comboBox.setCurrentIndex(logicalIndex)
780
- self.comboBox.blockSignals(True)
781
- valuesUnique = self.model._df.iloc[:, logicalIndex].unique()
782
- if logicalIndex == 0:
783
- valuesUnique = ['Sort: 0-9', 'Sort: 9-0']
784
- elif logicalIndex == 2:
785
- valuesUnique = ['Exclude N/A', 'Only N/A', 'Sort: A-Z', 'Sort: Z-A']
786
- elif logicalIndex == 3:
787
- valuesUnique = ['Sort: A-Z', 'Sort: Z-A']
788
- elif logicalIndex == 4 or logicalIndex == 5:
789
- valuesUnique = ['Exclude N/A', 'Only N/A', 'Sort: A-Z', 'Sort: Z-A']
790
-
791
- actionAll = QtWidgets.QAction("All", self)
792
- actionAll.triggered.connect(self.on_actionAll_triggered)
793
- self.menuValues.addAction(actionAll)
794
- self.menuValues.addSeparator()
795
- for actionNumber, actionName in enumerate(sorted(list(set(valuesUnique)))):
796
- action = QtWidgets.QAction(actionName, self)
797
- self.signalMapper.setMapping(action, actionNumber)
798
- action.triggered.connect(self.signalMapper.map)
799
- self.menuValues.addAction(action)
800
- self.signalMapper.mapped.connect(self.on_signalMapper_mapped)
801
- headerPos = self.ncbi_table.mapToGlobal(self.horizontalHeader.pos())
802
- posY = headerPos.y() + self.horizontalHeader.height()
803
- posX = headerPos.x() + self.horizontalHeader.sectionViewportPosition(logicalIndex)
804
-
805
- self.menuValues.exec_(QtCore.QPoint(posX, posY))
806
- except Exception as e:
807
- logger.critical("Error in on_view_horizontalHeader_sectionClicked() in ncbi tool.")
808
- logger.critical(e)
809
- logger.critical(traceback.format_exc())
810
- msgBox = QtWidgets.QMessageBox()
811
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
812
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
813
- msgBox.setWindowTitle("Fatal Error")
814
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
815
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
816
- msgBox.exec()
817
-
818
-
819
- exit(-1)
820
-
821
- @QtCore.pyqtSlot()
822
- def on_actionAll_triggered(self):
823
- try:
824
- filterColumn = self.logicalIndex
825
- self.proxy.setFilter("", filterColumn)
826
- except Exception as e:
827
- logger.critical("Error in on_actionAll_triggered() in ncbi tool.")
828
- logger.critical(e)
829
- logger.critical(traceback.format_exc())
830
- msgBox = QtWidgets.QMessageBox()
831
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
832
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
833
- msgBox.setWindowTitle("Fatal Error")
834
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
835
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
836
- msgBox.exec()
837
-
838
-
839
- exit(-1)
840
-
841
- @QtCore.pyqtSlot(int)
842
- def on_signalMapper_mapped(self, i):
843
- try:
844
- indices = self.ncbi_table.selectionModel().selectedRows()
845
- #stringAction = self.signalMapper.mapping(i).text()
846
- if self.logicalIndex == 0:
847
- if i == 0:
848
- self.model.sort(self.logicalIndex, QtCore.Qt.DescendingOrder)
849
- else:
850
- self.model.sort(self.logicalIndex, QtCore.Qt.AscendingOrder)
851
- elif self.logicalIndex == 3:
852
- if i == 0:
853
- self.model.sort(self.logicalIndex, QtCore.Qt.DescendingOrder)
854
- else:
855
- self.model.sort(self.logicalIndex, QtCore.Qt.AscendingOrder)
856
- elif self.logicalIndex == 2 or self.logicalIndex == 4 or self.logicalIndex == 5:
857
- if i == 0:
858
- stringAction = "(?!^N/A$)(^.*$)"
859
- filterColumn = self.logicalIndex
860
- self.proxy.setFilter(stringAction, filterColumn)
861
- elif i == 1:
862
- stringAction = "N/A"
863
- filterColumn = self.logicalIndex
864
- self.proxy.setFilter(stringAction, filterColumn)
865
- elif i == 2:
866
- self.model.sort(self.logicalIndex, QtCore.Qt.DescendingOrder)
867
- else:
868
- self.model.sort(self.logicalIndex, QtCore.Qt.AscendingOrder)
869
- elif self.logicalIndex == 6:
870
- stringAction = self.signalMapper.mapping(i).text()
871
- filterColumn = self.logicalIndex
872
- self.proxy.setFilter(stringAction, filterColumn)
873
- except Exception as e:
874
- logger.critical("Error in on_signalMapper_mapped() in ncbi tool.")
875
- logger.critical(e)
876
- logger.critical(traceback.format_exc())
877
- msgBox = QtWidgets.QMessageBox()
878
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
879
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
880
- msgBox.setWindowTitle("Fatal Error")
881
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
882
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
883
- msgBox.exec()
884
-
885
-
886
- exit(-1)
887
-
888
- @QtCore.pyqtSlot()
889
- def download_files_wrapper(self):
890
- try:
891
- self.progressBar.setValue(0)
892
-
893
- #make sure rows are present in table
894
- if self.df.shape[0] == 0:
895
- msgBox = QtWidgets.QMessageBox()
896
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
897
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
898
- msgBox.setWindowTitle("No Query Results")
899
- msgBox.setText("Please run an NCBI query to fill the table with results to choose from!")
900
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
901
- msgBox.exec()
902
-
903
- return
904
-
905
- #make sure user has selected at least one row
906
- indices = self.ncbi_table.selectionModel().selectedRows()
907
- if len(indices) == 0:
908
- msgBox = QtWidgets.QMessageBox()
909
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
910
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
911
- msgBox.setWindowTitle("No Rows Selected")
912
- msgBox.setText("Please select rows from the table!")
913
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
914
- msgBox.exec()
915
-
916
- return
917
-
918
-
919
- threadCount = QtCore.QThreadPool.globalInstance().maxThreadCount() # Get thread count
920
- if len(indices) > threadCount:
921
- msgBox = QtWidgets.QMessageBox()
922
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
923
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
924
- msgBox.setWindowTitle("Too Many Selections!")
925
- msgBox.setText("You only have " + str(threadCount) + " threads avaiable to download with.\n\nPlease select " + str(threadCount) + " or fewer rows.")
926
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
927
-
928
- msgBox.exec()
929
-
930
- return
931
-
932
-
933
- #make sure file type is selected
934
- if self.gbff_checkbox.isChecked() == False and self.fna_checkbox.isChecked() == False:
935
- msgBox = QtWidgets.QMessageBox()
936
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
937
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
938
- msgBox.setWindowTitle("No File Type Selected")
939
- msgBox.setText("No file type selected. Please select the file types you want to download!")
940
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
941
- msgBox.exec()
942
-
943
- return
944
-
945
- self.download_files()
946
- except Exception as e:
947
- logger.critical("Error in download_files_wrapper() in ncbi tool.")
948
- logger.critical(e)
949
- logger.critical(traceback.format_exc())
950
- msgBox = QtWidgets.QMessageBox()
951
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
952
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
953
- msgBox.setWindowTitle("Fatal Error")
954
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
955
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
956
- msgBox.exec()
957
-
958
-
959
- exit(-1)
960
-
961
- ### When a new thread is started, spawn a label and progress bar for that thread and add it to the form layout.
962
- def on_thread_start(self, data):
963
- id = data # This is the id for the thread
964
- tmp_lbl = QtWidgets.QLabel()
965
- tmp_lbl.setText("Download(s) Started...")
966
- tmp_bar = QtWidgets.QProgressBar()
967
- self.labels[id] = tmp_lbl # Append thread label to list
968
- self.progressbars[id] = tmp_bar # Append thread label to list
969
- self.formLayout.addRow(tmp_lbl,tmp_bar) # Add label and bar to form layout
970
-
971
- ### When a thread is finished, update its label and progressbar for the last time
972
- def on_thread_finish(self,data):
973
- id = data[0]
974
- my_bool = data[1]
975
- if my_bool: # If thread finished succesfully
976
- self.progressbars[id].setValue(int(self.progressbars[id].maximum())) #Make sure progress bar is full
977
- self.labels[id].setText("Download(s) Complete!") #Make sure progress bar is full
978
- self.progressBar.setValue(int(self.progressBar.value()+1)) # Increment overall progress bar when a thread finishes
979
- QtWidgets.QApplication.processEvents() # Allow the progress bar to update
980
- else:
981
- self.progressBar.setMaximum(int(self.progressBar.maximum()-1)) # Subtract 1 from progress bar to reflect failed thread.
982
- msgBox = QtWidgets.QMessageBox()
983
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
984
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
985
- msgBox.setWindowTitle("Link Failed!")
986
- msgBox.setText("Failed to find a valid link for ID: " + str(id) + ". Please make sure this ID is available in the selected database.")
987
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
988
- msgBox.exec()
989
- return
990
-
991
-
992
- ### When a file download starts, update the label and progress bar
993
- def on_file_start(self, data):
994
- id = data[0] # This is the id for the thread
995
- prompt = data[1] # This is the prompt to set the label to
996
- maxval = int(data[2]) # This is the file size
997
- self.progressbars[id].setValue(0) # Set max value of progressbar
998
- self.progressbars[id].setMaximum(int(maxval/1e3)) # Set max value of progressbar
999
-
1000
- self.labels[id].setText(str(prompt)) # Set label text
1001
-
1002
- ## Taken from StackOverflow: https://stackoverflow.com/questions/57607072/update-pyqt-progress-from-another-thread-running-ftp-download
1003
- def on_progress_ready(self, data):
1004
- id = data[0] # This is the id for the thread
1005
- val = int(data[1]) # This is the increment
1006
- self.progressbars[id].setValue(int(self.progressbars[id].value() + val/1e3)) # Increment progress bar
1007
-
1008
- ### When a file download finishes, update the label and progress bar and add file name to list
1009
- def on_file_finish(self,data):
1010
- id = data[0] # This is the id for the thread
1011
- prompt = data[1] # This is the prompt to set the label to
1012
- file_name = data[2] # This is the filename
1013
- self.progressbars[id].setValue(int(self.progressbars[id].maximum())) # Make sure progress bar is set to max after finishing download
1014
- self.labels[id].setText(str(prompt))
1015
- self.files.append(str(file_name)) # Add filename to list of downloaded files
1016
-
1017
- def clean_bars(self):
1018
- for key in self.progressbars:
1019
- label = self.formLayout.labelForField(self.progressbars[key])
1020
- if label is not None:
1021
- label.deleteLater()
1022
- self.progressbars[key].deleteLater()
1023
-
1024
- self.labels.clear()
1025
- self.progressbars.clear()
1026
- self.threads.clear()
1027
- def clear_layout(self):
1028
- for i in reversed(range(self.formLayout.count())):
1029
- self.formLayout.itemAt(i).widget().deleteLater()
1030
-
1031
- ## Taken from StackOverflow: https://stackoverflow.com/questions/57607072/update-pyqt-progress-from-another-thread-running-ftp-download
1032
- def download_files(self):
1033
- try:
1034
- self.labels = {} # Key is the ID, value is the QLabel
1035
- self.progressbars = {} # Key is the ID, value is the QProgressBar
1036
- self.threads = {} # Key is the ID, value is the QThread
1037
- indices = self.ncbi_table.selectionModel().selectedRows()
1038
- len_ind = len(indices) # Get number of rows
1039
- self.progressBar.setMaximum(int(len_ind)) # Set overall progress bar to be equal to number of rows being downloaded
1040
- if len_ind == 0:
1041
- return
1042
- if self.genbank_checkbox.isChecked() == False and self.refseq_checkbox.isChecked() == False:
1043
- return
1044
- self.progressLabel.setText("Download(s) Started...") # Update progresslabel
1045
- self.files = [] # Initialize list to hold downloaded files
1046
- for index in indices: # For selected row(s) in table
1047
- NewIndex = self.ncbi_table.model().index(index.row(), 0) # Get index of selected row
1048
- id = self.ncbi_table.model().data(NewIndex) # Get ID from selected row
1049
- dirs = [] # Initialize list to hold links to ftp directories
1050
- if self.genbank_checkbox.isChecked():
1051
- genbank_ftp = self.genbank_ftp_dict[int(id)]
1052
- dirs.append(genbank_ftp)
1053
- else:
1054
- refseq_ftp = self.refseq_ftp_dict[int(id)]
1055
- dirs.append(refseq_ftp)
1056
- for dir in dirs: # Loop through the ftp links
1057
- url = str(dir).replace('ftp://ftp.ncbi.nlm.nih.gov', '') # Create new link
1058
- downloader = DownloadThread(parent=self,url=url,id=id) # Create a thread for the download
1059
- downloader.started.connect(self.on_thread_start) # Connect signal to function
1060
- downloader.finished.connect(self.on_thread_finish) # Connect signal to function
1061
- downloader.file_started.connect(self.on_file_start)
1062
- downloader.file_finished.connect(self.on_file_finish)
1063
- downloader.data_progress.connect(self.on_progress_ready) # Connect signal to function
1064
- self.threads[url] = downloader # Add thread to dictionary using the url as a key
1065
- downloader.start()
1066
- """ Make sure all threads are done before checking to see if files were downloaded """
1067
- while len([self.threads[t] for t in self.threads if self.threads[t].isRunning()]) > 0:
1068
- time.sleep(0.1)
1069
- QtWidgets.QApplication.processEvents()
1070
- self.progressBar.setValue(int(self.progressBar.maximum())) # Set progressBar to maximum
1071
- self.progressLabel.setText("Download(s) Complete!") # Set progressBar to maximum
1072
-
1073
- self.clean_bars() # Clear out all the bars now that downloading is done
1074
-
1075
- for i in range(len(self.files)):
1076
- self.files[i] = self.files[i].replace('.gz', '')
1077
- if platform.system() == 'Windows':
1078
- self.files[i] = self.files[i][self.files[i].rfind("\\")+1:]
1079
- else:
1080
- self.files[i] = self.files[i][self.files[i].rfind("/") + 1:]
1081
-
1082
- if len(self.files) > 0:
1083
- self.rename_files(self.files)
1084
- else:
1085
- self.clear_layout()
1086
- self.clean_bars() # Clear out all the bars since the download failed
1087
- msgBox = QtWidgets.QMessageBox()
1088
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1089
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1090
- msgBox.setWindowTitle("No Files Downloaded")
1091
- msgBox.setText("No files were downloaded from the selected NCBI files. Please make sure the selected files are available in the database selected.")
1092
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
1093
- msgBox.exec()
1094
- return
1095
-
1096
- except Exception as e:
1097
- logger.critical("Error in download_files() in ncbi tool.")
1098
- logger.critical(e)
1099
- logger.critical(traceback.format_exc())
1100
- msgBox = QtWidgets.QMessageBox()
1101
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1102
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1103
- msgBox.setWindowTitle("Fatal Error")
1104
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1105
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1106
- msgBox.exec()
1107
-
1108
-
1109
- exit(-1)
1110
-
1111
- @QtCore.pyqtSlot()
1112
- def select_all(self):
1113
- try:
1114
- if self.all_rows.isChecked():
1115
- self.ncbi_table.selectAll()
1116
- else:
1117
- self.ncbi_table.clearSelection()
1118
- except Exception as e:
1119
- logger.critical("Error in select_all() in ncbi tool.")
1120
- logger.critical(e)
1121
- logger.critical(traceback.format_exc())
1122
- msgBox = QtWidgets.QMessageBox()
1123
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1124
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1125
- msgBox.setWindowTitle("Fatal Error")
1126
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1127
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1128
- msgBox.exec()
1129
-
1130
-
1131
- exit(-1)
1132
-
1133
- def rename_files(self, files):
1134
- try:
1135
- msgBox = QtWidgets.QMessageBox()
1136
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1137
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Question)
1138
- msgBox.setWindowTitle("Rename Files")
1139
- msgBox.setText("Would you like to rename the downloaded files?")
1140
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Yes)
1141
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.No)
1142
- msgBox.exec()
1143
-
1144
- if msgBox.result() == QtWidgets.QMessageBox.Yes:
1145
- self.rename_window.rename_table.setRowCount(len(files))
1146
- cnt = 0
1147
- for file in files:
1148
- item = QtWidgets.QTableWidgetItem(file)
1149
- item.setFlags(QtCore.Qt.ItemIsEnabled)
1150
- self.rename_window.rename_table.setItem(cnt, 0, item)
1151
-
1152
- self.rename_window.rename_table.setCellWidget(cnt, 1, QtWidgets.QLineEdit())
1153
- cnt += 1
1154
-
1155
- header = self.rename_window.rename_table.horizontalHeader()
1156
- self.rename_window.rename_table.resizeColumnsToContents()
1157
- header.setSectionResizeMode(0, QtWidgets.QHeaderView.ResizeToContents)
1158
- header.setSectionResizeMode(1, QtWidgets.QHeaderView.Stretch)
1159
- self.rename_window.centerUI()
1160
- self.rename_window.show()
1161
- self.rename_window.activateWindow()
1162
- else:
1163
- GlobalSettings.mainWindow.fill_annotation_dropdown()
1164
- self.goToPrompt.show()
1165
- self.goToPrompt.activateWindow()
1166
- except Exception as e:
1167
- logger.critical("Error in rename_files() in ncbi tool.")
1168
- logger.critical(e)
1169
- logger.critical(traceback.format_exc())
1170
- msgBox = QtWidgets.QMessageBox()
1171
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1172
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1173
- msgBox.setWindowTitle("Fatal Error")
1174
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1175
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1176
- msgBox.exec()
1177
-
1178
-
1179
- exit(-1)
1180
-
1181
- def rename_go_back(self):
1182
- try:
1183
- self.rename_window.close()
1184
- except Exception as e:
1185
- logger.critical("Error in rename_go_back() in ncbi tool.")
1186
- logger.critical(e)
1187
- logger.critical(traceback.format_exc())
1188
- msgBox = QtWidgets.QMessageBox()
1189
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1190
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1191
- msgBox.setWindowTitle("Fatal Error")
1192
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1193
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1194
- msgBox.exec()
1195
- exit(-1)
1196
-
1197
- def submit_rename(self):
1198
- try:
1199
- #loop through columns and rename the files
1200
- for row in range(self.rename_window.rename_table.rowCount()):
1201
- orig = str(self.rename_window.rename_table.item(row, 0).text())
1202
- new = str(self.rename_window.rename_table.cellWidget(row, 1).text())
1203
- if new != "":
1204
- if orig.find(".gbff")!= -1:
1205
- if new.find(".") != -1:
1206
- new = new[:new.find(".")]
1207
- new = new + ".gbff"
1208
- elif orig.find(".fna") != -1:
1209
- if new.find(".") != -1:
1210
- new = new[:new.find(".")]
1211
- new = new + ".fna"
1212
-
1213
- if platform.system() == "Windows":
1214
- if new.find(".gbff") != -1:
1215
- if os.path.isfile(GlobalSettings.CSPR_DB + "\\GBFF\\" + new) == False:
1216
- os.rename(GlobalSettings.CSPR_DB + "\\GBFF\\" + orig, GlobalSettings.CSPR_DB + "\\GBFF\\" + new)
1217
- else:
1218
- msgBox = QtWidgets.QMessageBox()
1219
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1220
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1221
- msgBox.setWindowTitle("Renaming Error")
1222
- msgBox.setText("The filename: " + str(new) + " already exists. Please use a different name." )
1223
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
1224
- msgBox.exec()
1225
- return
1226
- else:
1227
- if os.path.isfile(GlobalSettings.CSPR_DB + "\\FNA\\" + new) == False:
1228
- os.rename(GlobalSettings.CSPR_DB + "\\FNA\\" + orig, GlobalSettings.CSPR_DB + "\\FNA\\" + new)
1229
- else:
1230
- msgBox = QtWidgets.QMessageBox()
1231
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1232
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1233
- msgBox.setWindowTitle("Renaming Error")
1234
- msgBox.setText("The filename: " + str(new) + " already exists. Please use a different name." )
1235
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
1236
- msgBox.exec()
1237
- return
1238
-
1239
- else:
1240
- #unix cannot have spaces in paths
1241
- new = new.replace(" ","")
1242
- if new.find(".gbff") != -1:
1243
- if os.path.isfile(GlobalSettings.CSPR_DB + "/GBFF/" + new) == False:
1244
- os.rename(GlobalSettings.CSPR_DB + "/GBFF/" + orig, GlobalSettings.CSPR_DB + "/GBFF/" + new)
1245
- else:
1246
- msgBox = QtWidgets.QMessageBox()
1247
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1248
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1249
- msgBox.setWindowTitle("Renaming Error")
1250
- msgBox.setText(
1251
- "The filename: " + str(new) + " already exists. Please use a different name.")
1252
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
1253
- msgBox.exec()
1254
- return
1255
- else:
1256
- if os.path.isfile(GlobalSettings.CSPR_DB + "/FNA/" + new) == False:
1257
- os.rename(GlobalSettings.CSPR_DB + "/FNA/" + orig, GlobalSettings.CSPR_DB + "/FNA/" + new)
1258
- else:
1259
- msgBox = QtWidgets.QMessageBox()
1260
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1261
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1262
- msgBox.setWindowTitle("Renaming Error")
1263
- msgBox.setText(
1264
- "The filename: " + str(new) + " already exists. Please use a different name.")
1265
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
1266
- msgBox.exec()
1267
- return
1268
-
1269
- self.rename_window.rename_table.setRowCount(0)
1270
- self.rename_window.close()
1271
- GlobalSettings.mainWindow.fill_annotation_dropdown()
1272
- self.goToPrompt.centerUI()
1273
- self.goToPrompt.show()
1274
- self.goToPrompt.activateWindow()
1275
- except Exception as e:
1276
- logger.critical("Error in submit_rename() in ncbi tool.")
1277
- logger.critical(e)
1278
- logger.critical(traceback.format_exc())
1279
- msgBox = QtWidgets.QMessageBox()
1280
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1281
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1282
- msgBox.setWindowTitle("Fatal Error")
1283
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1284
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1285
- msgBox.exec()
1286
-
1287
-
1288
- exit(-1)
1289
-
1290
- def stay(self):
1291
- try:
1292
- self.goToPrompt.hide()
1293
- except Exception as e:
1294
- logger.critical("Error in stay() in ncbi tool.")
1295
- logger.critical(e)
1296
- logger.critical(traceback.format_exc())
1297
- msgBox = QtWidgets.QMessageBox()
1298
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1299
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1300
- msgBox.setWindowTitle("Fatal Error")
1301
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1302
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1303
- msgBox.exec()
1304
-
1305
-
1306
- exit(-1)
1307
-
1308
- def close(self):
1309
- try:
1310
- self.hide()
1311
- self.goToPrompt.hide()
1312
- except Exception as e:
1313
- logger.critical("Error in close() in ncbi tool.")
1314
- logger.critical(e)
1315
- logger.critical(traceback.format_exc())
1316
- msgBox = QtWidgets.QMessageBox()
1317
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1318
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1319
- msgBox.setWindowTitle("Fatal Error")
1320
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1321
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1322
- msgBox.exec()
1323
-
1324
-
1325
- exit(-1)
1326
-
1327
-
1328
- class goToPrompt(QtWidgets.QMainWindow):
1329
- def __init__(self):
1330
- try:
1331
- super(goToPrompt, self).__init__()
1332
- uic.loadUi(GlobalSettings.appdir + 'ncbi_nav_page.ui', self)
1333
- self.setWindowTitle("Navigate")
1334
- self.setWindowIcon(Qt.QIcon(GlobalSettings.appdir + "cas9image.ico"))
1335
- self.label.setText("Successfully downloaded file(s) to the CASPERdb directory:\n" + GlobalSettings.CSPR_DB)
1336
- self.label.setAlignment(QtCore.Qt.AlignCenter)
1337
- groupbox_style = """
1338
- QGroupBox:title{subcontrol-origin: margin;
1339
- left: 10px;
1340
- padding: 0 5px 0 5px;}
1341
- QGroupBox#groupBox{border: 2px solid rgb(111,181,110);
1342
- border-radius: 9px;
1343
- font: bold 14pt 'Arial';
1344
- margin-top: 10px;}"""
1345
- self.groupBox.setStyleSheet(groupbox_style)
1346
-
1347
- self.scaleUI()
1348
- except Exception as e:
1349
- logger.critical("Error initializing goToPrompt class in ncbi tool.")
1350
- logger.critical(e)
1351
- logger.critical(traceback.format_exc())
1352
- msgBox = QtWidgets.QMessageBox()
1353
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1354
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1355
- msgBox.setWindowTitle("Fatal Error")
1356
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1357
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1358
- msgBox.exec()
1359
-
1360
-
1361
- exit(-1)
1362
-
1363
- #scale UI based on current screen
1364
- def scaleUI(self):
1365
- try:
1366
- self.repaint()
1367
- QtWidgets.QApplication.processEvents()
1368
-
1369
- screen = self.screen()
1370
- dpi = screen.physicalDotsPerInch()
1371
- width = screen.geometry().width()
1372
- height = screen.geometry().height()
1373
-
1374
- # font scaling
1375
- fontSize = 12
1376
- self.centralWidget().setStyleSheet("font: " + str(fontSize) + "pt 'Arial';")
1377
-
1378
- self.adjustSize()
1379
-
1380
- currentWidth = self.size().width()
1381
- currentHeight = self.size().height()
1382
-
1383
- # window scaling
1384
- # 1920x1080 => 550x200
1385
- scaledWidth = int((width * 575) / 1920)
1386
- scaledHeight = int((height * 225) / 1080)
1387
-
1388
- if scaledHeight < currentHeight:
1389
- scaledHeight = currentHeight
1390
- if scaledWidth < currentWidth:
1391
- scaledWidth = currentWidth
1392
-
1393
- screen = QtWidgets.QApplication.desktop().screenNumber(QtWidgets.QApplication.desktop().cursor().pos())
1394
- centerPoint = QtWidgets.QApplication.desktop().screenGeometry(screen).center()
1395
- x = centerPoint.x()
1396
- y = centerPoint.y()
1397
- x = x - (math.ceil(scaledWidth / 2))
1398
- y = y - (math.ceil(scaledHeight / 2))
1399
- self.setGeometry(x, y, scaledWidth, scaledHeight)
1400
-
1401
- self.repaint()
1402
- QtWidgets.QApplication.processEvents()
1403
-
1404
- except Exception as e:
1405
- logger.critical("Error in scaleUI() in goToPrompt in NCBI tool.")
1406
- logger.critical(e)
1407
- logger.critical(traceback.format_exc())
1408
- msgBox = QtWidgets.QMessageBox()
1409
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1410
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1411
- msgBox.setWindowTitle("Fatal Error")
1412
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1413
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1414
- msgBox.exec()
1415
-
1416
-
1417
- exit(-1)
1418
-
1419
- #center UI on current screen
1420
- def centerUI(self):
1421
- try:
1422
- self.repaint()
1423
- QtWidgets.QApplication.processEvents()
1424
-
1425
- # center window on current screen
1426
- width = self.width()
1427
- height = self.height()
1428
- screen = QtWidgets.QApplication.desktop().screenNumber(QtWidgets.QApplication.desktop().cursor().pos())
1429
- centerPoint = QtWidgets.QApplication.desktop().screenGeometry(screen).center()
1430
- x = centerPoint.x()
1431
- y = centerPoint.y()
1432
- x = x - (math.ceil(width / 2))
1433
- y = y - (math.ceil(height / 2))
1434
- self.setGeometry(x, y, width, height)
1435
-
1436
- self.repaint()
1437
- QtWidgets.QApplication.processEvents()
1438
- except Exception as e:
1439
- logger.critical("Error in centerUI() in goToPrompt in NCBI tool.")
1440
- logger.critical(e)
1441
- logger.critical(traceback.format_exc())
1442
- msgBox = QtWidgets.QMessageBox()
1443
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1444
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1445
- msgBox.setWindowTitle("Fatal Error")
1446
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1447
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1448
- msgBox.exec()
1449
-
1450
-
1451
- exit(-1)
1452
-
1453
-
1454
- class rename_window(QtWidgets.QMainWindow):
1455
- def __init__(self):
1456
- try:
1457
- super(rename_window, self).__init__()
1458
- uic.loadUi(GlobalSettings.appdir + "ncbi_rename_window.ui", self)
1459
- self.setWindowIcon(Qt.QIcon(GlobalSettings.appdir + "cas9image.ico"))
1460
- self.setWindowTitle("Rename Files")
1461
- self.rename_table.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents)
1462
- self.rename_table.setColumnCount(2)
1463
- self.rename_table.setHorizontalHeaderLabels(['Original Filename', 'New Filename'])
1464
-
1465
- self.scaleUI()
1466
- except Exception as e:
1467
- logger.critical("Error initializing rename_window class in ncbi tool.")
1468
- logger.critical(e)
1469
- logger.critical(traceback.format_exc())
1470
- msgBox = QtWidgets.QMessageBox()
1471
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1472
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1473
- msgBox.setWindowTitle("Fatal Error")
1474
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1475
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1476
- msgBox.exec()
1477
-
1478
-
1479
- exit(-1)
1480
-
1481
- #scale UI based on current screen
1482
- def scaleUI(self):
1483
- try:
1484
- self.repaint()
1485
- QtWidgets.QApplication.processEvents()
1486
-
1487
- screen = self.screen()
1488
- dpi = screen.physicalDotsPerInch()
1489
- width = screen.geometry().width()
1490
- height = screen.geometry().height()
1491
-
1492
- # font scaling
1493
- fontSize = 12
1494
- self.centralWidget().setStyleSheet("font: " + str(fontSize) + "pt 'Arial';")
1495
-
1496
- self.adjustSize()
1497
-
1498
- currentWidth = self.size().width()
1499
- currentHeight = self.size().height()
1500
-
1501
- # window scaling
1502
- # 1920x1080 => 550x200
1503
- scaledWidth = int((width * 575) / 1920)
1504
- scaledHeight = int((height * 300) / 1080)
1505
-
1506
- if scaledHeight < currentHeight:
1507
- scaledHeight = currentHeight
1508
- if scaledWidth < currentWidth:
1509
- scaledWidth = currentWidth
1510
-
1511
- screen = QtWidgets.QApplication.desktop().screenNumber(QtWidgets.QApplication.desktop().cursor().pos())
1512
- centerPoint = QtWidgets.QApplication.desktop().screenGeometry(screen).center()
1513
- x = centerPoint.x()
1514
- y = centerPoint.y()
1515
- x = x - (math.ceil(scaledWidth / 2))
1516
- y = y - (math.ceil(scaledHeight / 2))
1517
- self.setGeometry(x, y, scaledWidth, scaledHeight)
1518
-
1519
- self.repaint()
1520
- QtWidgets.QApplication.processEvents()
1521
-
1522
- except Exception as e:
1523
- logger.critical("Error in scaleUI() in rename window in NCBI tool.")
1524
- logger.critical(e)
1525
- logger.critical(traceback.format_exc())
1526
- msgBox = QtWidgets.QMessageBox()
1527
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1528
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1529
- msgBox.setWindowTitle("Fatal Error")
1530
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1531
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1532
- msgBox.exec()
1533
-
1534
-
1535
- exit(-1)
1536
-
1537
- #center UI on current screen
1538
- def centerUI(self):
1539
- try:
1540
- self.repaint()
1541
- QtWidgets.QApplication.processEvents()
1542
-
1543
- # center window on current screen
1544
- width = self.width()
1545
- height = self.height()
1546
- screen = QtWidgets.QApplication.desktop().screenNumber(QtWidgets.QApplication.desktop().cursor().pos())
1547
- centerPoint = QtWidgets.QApplication.desktop().screenGeometry(screen).center()
1548
- x = centerPoint.x()
1549
- y = centerPoint.y()
1550
- x = x - (math.ceil(width / 2))
1551
- y = y - (math.ceil(height / 2))
1552
- self.setGeometry(x, y, width, height)
1553
-
1554
- self.repaint()
1555
- QtWidgets.QApplication.processEvents()
1556
- except Exception as e:
1557
- logger.critical("Error in centerUI() in rename window in NCBI tool.")
1558
- logger.critical(e)
1559
- logger.critical(traceback.format_exc())
1560
- msgBox = QtWidgets.QMessageBox()
1561
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1562
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1563
- msgBox.setWindowTitle("Fatal Error")
1564
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1565
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1566
- msgBox.exec()
1567
-
1568
-
1569
- exit(-1)
1570
-
1571
-
1572
- #loading window UI class for when data is loading
1573
- class loading_window(QtWidgets.QMainWindow):
1574
- def __init__(self):
1575
- try:
1576
- super(loading_window, self).__init__()
1577
- uic.loadUi(GlobalSettings.appdir + "loading_data_form.ui", self)
1578
- self.loading_bar.setValue(0)
1579
- self.setWindowTitle("Loading NCBI Data")
1580
- self.setWindowIcon(Qt.QIcon(GlobalSettings.appdir + "cas9image.ico"))
1581
- self.scaleUI()
1582
- except Exception as e:
1583
- logger.critical("Error initializing loading_window class in ncbi tool.")
1584
- logger.critical(e)
1585
- logger.critical(traceback.format_exc())
1586
- msgBox = QtWidgets.QMessageBox()
1587
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1588
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1589
- msgBox.setWindowTitle("Fatal Error")
1590
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1591
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1592
- msgBox.exec()
1593
-
1594
-
1595
- exit(-1)
1596
-
1597
- #scale UI based on current screen
1598
- def scaleUI(self):
1599
- try:
1600
- self.repaint()
1601
- QtWidgets.QApplication.processEvents()
1602
-
1603
- screen = self.screen()
1604
- dpi = screen.physicalDotsPerInch()
1605
- width = screen.geometry().width()
1606
- height = screen.geometry().height()
1607
-
1608
- # font scaling
1609
- fontSize = 12
1610
- self.centralWidget().setStyleSheet("font: " + str(fontSize) + "pt 'Arial';")
1611
-
1612
- self.adjustSize()
1613
-
1614
- currentWidth = self.size().width()
1615
- currentHeight = self.size().height()
1616
-
1617
- # scale/center window
1618
- scaledWidth = int((width * 450) / 1920)
1619
- scaledHeight = int((height * 125) / 1080)
1620
-
1621
- if scaledHeight < currentHeight:
1622
- scaledHeight = currentHeight
1623
- if scaledWidth < currentWidth:
1624
- scaledWidth = currentWidth
1625
-
1626
- screen = QtWidgets.QApplication.desktop().screenNumber(QtWidgets.QApplication.desktop().cursor().pos())
1627
- centerPoint = QtWidgets.QApplication.desktop().screenGeometry(screen).center()
1628
- x = centerPoint.x()
1629
- y = centerPoint.y()
1630
- x = x - (math.ceil(scaledWidth / 2))
1631
- y = y - (math.ceil(scaledHeight / 2))
1632
- self.setGeometry(x, y, scaledWidth, scaledHeight)
1633
-
1634
- self.repaint()
1635
- QtWidgets.QApplication.processEvents()
1636
- except Exception as e:
1637
- logger.critical("Error in scaleUI() in loading_window() class in ncbi tool.")
1638
- logger.critical(e)
1639
- logger.critical(traceback.format_exc())
1640
- msgBox = QtWidgets.QMessageBox()
1641
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1642
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1643
- msgBox.setWindowTitle("Fatal Error")
1644
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1645
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1646
- msgBox.exec()
1647
-
1648
-
1649
- exit(-1)
1650
-
1651
- #center UI on current screen
1652
- def centerUI(self):
1653
- try:
1654
- self.repaint()
1655
- QtWidgets.QApplication.processEvents()
1656
-
1657
- width = self.width()
1658
- height = self.height()
1659
- #scale/center window
1660
- screen = QtWidgets.QApplication.desktop().screenNumber(QtWidgets.QApplication.desktop().cursor().pos())
1661
- centerPoint = QtWidgets.QApplication.desktop().screenGeometry(screen).center()
1662
- x = centerPoint.x()
1663
- y = centerPoint.y()
1664
- x = x - (math.ceil(width / 2))
1665
- y = y - (math.ceil(height / 2))
1666
- self.setGeometry(x, y, width, height)
1667
-
1668
- self.repaint()
1669
- QtWidgets.QApplication.processEvents()
1670
- except Exception as e:
1671
- logger.critical("Error in centerUI() in loading_window() class in ncbi tool.")
1672
- logger.critical(e)
1673
- logger.critical(traceback.format_exc())
1674
- msgBox = QtWidgets.QMessageBox()
1675
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1676
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1677
- msgBox.setWindowTitle("Fatal Error")
1678
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1679
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1680
- msgBox.exec()
1681
-
1682
-
1683
- exit(-1)
1684
-
1685
-
1686
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
linux.spec → scripts/linux.spec RENAMED
File without changes
mac.spec → scripts/mac.spec RENAMED
File without changes
windows.spec → scripts/windows.spec RENAMED
File without changes
windowsInstallerScript.iss → scripts/windowsInstallerScript.iss RENAMED
File without changes
CASPER_main.ui → ui/CASPER_main.ui RENAMED
File without changes
NewGenome.ui → ui/NewGenome.ui RENAMED
File without changes
annotation_details.ui → ui/annotation_details.ui RENAMED
File without changes
closing_window.ui → ui/closing_window.ui RENAMED
File without changes
cotargeting.ui → ui/cotargeting.ui RENAMED
File without changes
export_tool.ui → ui/export_tool.ui RENAMED
File without changes
filter_options.ui → ui/filter_options.ui RENAMED
File without changes
generate_library.ui → ui/generate_library.ui RENAMED
File without changes
loading.ui → ui/loading.ui RENAMED
File without changes
loading_data_form.ui → ui/loading_data_form.ui RENAMED
File without changes
mt.ui → ui/mt.ui RENAMED
File without changes
multitargeting_sql_settings.ui → ui/multitargeting_sql_settings.ui RENAMED
File without changes
multitargeting_stats.ui → ui/multitargeting_stats.ui RENAMED
File without changes
name_form.ui → ui/name_form.ui RENAMED
File without changes
ncbi.ui → ui/ncbi.ui RENAMED
File without changes
ncbi_nav_page.ui → ui/ncbi_nav_page.ui RENAMED
File without changes
ncbi_rename_window.ui → ui/ncbi_rename_window.ui RENAMED
File without changes
newendonuclease.ui → ui/newendonuclease.ui RENAMED
File without changes
newgenomenavigationpage.ui → ui/newgenomenavigationpage.ui RENAMED
File without changes
off_target.ui → ui/off_target.ui RENAMED
File without changes
pop.ui → ui/pop.ui RENAMED
File without changes
results.ui → ui/results.ui RENAMED
File without changes
scoring_window.ui → ui/scoring_window.ui RENAMED
File without changes
startupCASPER.ui → ui/startupCASPER.ui RENAMED
File without changes
Algorithms.py → utils/Algorithms.py RENAMED
@@ -3,7 +3,7 @@
3
  To interpret these run the class instance at the bottom of the file with the desired base64 representation into the
4
  decompress_tuple function."""
5
  from PyQt5 import QtWidgets, Qt, uic, QtCore, QtGui
6
- import GlobalSettings
7
 
8
  # Takes a QtTableWidget and returns a list of headers
9
  def get_table_headers(table):
 
3
  To interpret these run the class instance at the bottom of the file with the desired base64 representation into the
4
  decompress_tuple function."""
5
  from PyQt5 import QtWidgets, Qt, uic, QtCore, QtGui
6
+ import models.GlobalSettings as GlobalSettings
7
 
8
  # Takes a QtTableWidget and returns a list of headers
9
  def get_table_headers(table):
ui_utils.py → utils/ui.py RENAMED
@@ -1,11 +1,41 @@
1
  from PyQt5 import QtWidgets, QtGui, QtCore
 
2
  import math
3
- from error_handling import show_error
4
- import GlobalSettings
5
 
6
- def scale_ui(window, base_width=1920, base_height=1080, font_size=12, header_font_size=30):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  try:
8
-
9
  window.repaint()
10
  QtWidgets.QApplication.processEvents()
11
 
@@ -15,11 +45,16 @@ def scale_ui(window, base_width=1920, base_height=1080, font_size=12, header_fon
15
 
16
  # Font scaling
17
  window.centralWidget().setStyleSheet(f"font: {font_size}pt 'Arial';")
18
- window.menuBar().setStyleSheet(f"font: {font_size}pt 'Arial';")
 
19
 
20
  # Header scaling
21
- if hasattr(window, 'label_8'): # Check if the window has 'label_8' attribute
22
- window.label_8.setStyleSheet(f"font: bold {header_font_size}pt 'Arial';")
 
 
 
 
23
 
24
  window.adjustSize()
25
 
@@ -31,8 +66,8 @@ def scale_ui(window, base_width=1920, base_height=1080, font_size=12, header_fon
31
  window.logo.setPixmap(QtGui.QPixmap(GlobalSettings.appdir + "assets/CASPER-logo.jpg"))
32
 
33
  # Window resize and center
34
- scaledWidth = int((width * 1150) / base_width)
35
- scaledHeight = int((height * 650) / base_height)
36
 
37
  if scaledHeight < currentHeight:
38
  scaledHeight = currentHeight
@@ -74,6 +109,5 @@ def center_ui(window):
74
  window.setGeometry(x, y, width, height)
75
  window.repaint()
76
  except Exception as e:
77
- # Log the error or handle it as needed
78
  print(f"Error centering window: {e}")
79
 
 
1
  from PyQt5 import QtWidgets, QtGui, QtCore
2
+ import traceback
3
  import math
4
+ import models.GlobalSettings as GlobalSettings
 
5
 
6
+ logger = GlobalSettings.logger
7
+
8
+ def show_message(fontSize, icon, title, message, button=QtWidgets.QMessageBox.StandardButton.Close):
9
+ try:
10
+ msgBox = QtWidgets.QMessageBox()
11
+ msgBox.setStyleSheet(f"font: {fontSize}pt 'Arial'")
12
+ msgBox.setIcon(icon)
13
+ msgBox.setWindowTitle(title)
14
+ msgBox.setText(message)
15
+ msgBox.addButton(button)
16
+ msgBox.exec()
17
+ except Exception as e:
18
+ print(e)
19
+
20
+ def show_error(message, e):
21
+ try:
22
+ logger.critical(message)
23
+ logger.critical(e)
24
+ logger.critical(traceback.format_exc())
25
+
26
+ show_message(
27
+ fontSize=12,
28
+ icon=QtWidgets.QMessageBox.Icon.Critical,
29
+ title="Fatal Error",
30
+ message=f"Fatal Error:\n{str(e)}\n\nFor more information on this error, look at CASPER.log in the application folder."
31
+ )
32
+ except Exception as e:
33
+ print(e)
34
+
35
+ exit(-1)
36
+
37
+ def scale_ui(window, base_width=1920, base_height=1080, font_size=12, header_font_size=30, custom_scale_width=None, custom_scale_height=None):
38
  try:
 
39
  window.repaint()
40
  QtWidgets.QApplication.processEvents()
41
 
 
45
 
46
  # Font scaling
47
  window.centralWidget().setStyleSheet(f"font: {font_size}pt 'Arial';")
48
+ if hasattr(window, 'menuBar'):
49
+ window.menuBar().setStyleSheet(f"font: {font_size}pt 'Arial';")
50
 
51
  # Header scaling
52
+ # if hasattr(window, 'label_8'):
53
+ # window.label_8.setStyleSheet(f"font: bold {header_font_size}pt 'Arial';")
54
+
55
+ if hasattr(window, 'title'):
56
+ scaled_title_font_size = int(header_font_size * (width / base_width))
57
+ window.title.setStyleSheet(f"font: bold {scaled_title_font_size}pt 'Arial';")
58
 
59
  window.adjustSize()
60
 
 
66
  window.logo.setPixmap(QtGui.QPixmap(GlobalSettings.appdir + "assets/CASPER-logo.jpg"))
67
 
68
  # Window resize and center
69
+ scaledWidth = int((width * (custom_scale_width if custom_scale_width else 1150)) / base_width)
70
+ scaledHeight = int((height * (custom_scale_height if custom_scale_height else 650)) / base_height)
71
 
72
  if scaledHeight < currentHeight:
73
  scaledHeight = currentHeight
 
109
  window.setGeometry(x, y, width, height)
110
  window.repaint()
111
  except Exception as e:
 
112
  print(f"Error centering window: {e}")
113
 
utils/web.py ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import webbrowser
2
+ from utils.ui import show_error
3
+
4
+ def ncbi_blast_page():
5
+ open_webpage("https://blast.ncbi.nlm.nih.gov/Blast.cgi")
6
+
7
+ def ncbi_page():
8
+ open_webpage("https://www.ncbi.nlm.nih.gov/")
9
+
10
+ def repo_page():
11
+ open_webpage("https://github.com/TrinhLab/CASPERapp")
12
+
13
+ def open_webpage(url):
14
+ try:
15
+ webbrowser.open(url, new=2)
16
+ except Exception as e:
17
+ show_error("Error in open_webpage()", e)
AnnotationParser.py → views/AnnotationParser.py RENAMED
@@ -1,17 +1,15 @@
1
  ###############################################################################
2
- # This is the Annotation Parser File
3
  # INPUTS: inputs are the annotation files to parse. Currently, only gbff is supported.
4
  # OUTPUTS: the outputs are data structures that store the parsed data
5
  ################################################################################
6
 
7
  from PyQt5 import QtWidgets
8
  import gffutils
9
- import GlobalSettings
10
  import os
11
  from Bio import SeqIO
12
  import traceback
13
 
14
- #global logger
15
  logger = GlobalSettings.logger
16
 
17
  class Annotation_Parser:
@@ -94,9 +92,6 @@ class Annotation_Parser:
94
 
95
  exit(-1)
96
 
97
-
98
-
99
-
100
  ### The workhorse function of AnnotationParser, this searches the annotation file for the user's search and returns features matching the description.
101
  def genbank_search(self, queries, same_search):
102
  index_number = 0
 
1
  ###############################################################################
 
2
  # INPUTS: inputs are the annotation files to parse. Currently, only gbff is supported.
3
  # OUTPUTS: the outputs are data structures that store the parsed data
4
  ################################################################################
5
 
6
  from PyQt5 import QtWidgets
7
  import gffutils
8
+ import models.GlobalSettings as GlobalSettings
9
  import os
10
  from Bio import SeqIO
11
  import traceback
12
 
 
13
  logger = GlobalSettings.logger
14
 
15
  class Annotation_Parser:
 
92
 
93
  exit(-1)
94
 
 
 
 
95
  ### The workhorse function of AnnotationParser, this searches the annotation file for the user's search and returns features matching the description.
96
  def genbank_search(self, queries, same_search):
97
  index_number = 0
views/AnnotationWindow.py ADDED
@@ -0,0 +1,125 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import models.GlobalSettings as GlobalSettings
2
+ from utils.ui import show_error, scale_ui, center_ui
3
+ from PyQt5 import QtWidgets, QtCore, uic, Qt
4
+ from views.annotation_functions import *
5
+ import traceback
6
+
7
+ logger = GlobalSettings.logger
8
+
9
+ class AnnotationWindow(QtWidgets.QMainWindow):
10
+ def __init__(self, info_path):
11
+ super(AnnotationWindow, self).__init__()
12
+ uic.loadUi(GlobalSettings.appdir + 'ui/annotation_details.ui', self)
13
+ self.setWindowIcon(Qt.QIcon(GlobalSettings.appdir + "cas9image.ico"))
14
+ self.Submit_button.clicked.connect(self.submit)
15
+ self.Go_Back_Button.clicked.connect(self.go_Back)
16
+ self.select_all_checkbox.stateChanged.connect(self.select_all_genes)
17
+ self.fontSize = 12
18
+ self.mainWindow = ""
19
+ self.type = ""
20
+ self.mwfg = self.frameGeometry() ##Center window
21
+ self.cp = QtWidgets.QDesktopWidget().availableGeometry().center() ##Center window
22
+ self.switcher_table = [1, 1, 1, 1, 1, 1, 1, 1]
23
+ self.tableWidget.setHorizontalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
24
+ self.tableWidget.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
25
+ self.tableWidget.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
26
+ self.tableWidget.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
27
+ self.tableWidget.setAutoScroll(False)
28
+ self.tableWidget.horizontalHeader().sectionClicked.connect(self.table_sorting)
29
+
30
+ scale_ui(self, custom_scale_width=1150, custom_scale_height=650)
31
+
32
+ def table_sorting(self, logicalIndex):
33
+ try:
34
+ self.switcher_table[logicalIndex] *= -1
35
+ order = QtCore.Qt.DescendingOrder if self.switcher_table[logicalIndex] == -1 else QtCore.Qt.AscendingOrder
36
+ self.tableWidget.sortItems(logicalIndex, order)
37
+ except Exception as e:
38
+ show_error("table_sorting() in Annotation Window", e)
39
+
40
+ #submit selected rows for results to process
41
+ def submit(self):
42
+ try:
43
+ self.mainWindow.collect_table_data_nonkegg()
44
+ self.mainWindow.show() # Open main window back up
45
+ except Exception as e:
46
+ logger.critical("Error in submit() in AnnotationWindow.")
47
+ logger.critical(e)
48
+ logger.critical(traceback.format_exc())
49
+ show_error("submit() in AnnotationWindow", e)
50
+ finally:
51
+ self.hide() # Close annotation window regardless of success or failure
52
+
53
+ #go back to main
54
+ def go_Back(self):
55
+ try:
56
+ self.tableWidget.clear()
57
+ self.mainWindow.searches.clear()
58
+ self.tableWidget.setColumnCount(0)
59
+ self.mainWindow.show()
60
+ self.mainWindow.progressBar.setValue(0)
61
+ self.hide()
62
+ except Exception as e:
63
+ show_error("go_Back() in AnnotationWindow", e)
64
+ self.mainWindow.checkBoxes.clear()
65
+
66
+ # this is the connection for the select all checkbox - selects/deselects all the genes in the table
67
+ # this function is very similar to the other fill_table, it just works with the other types of annotation files
68
+ def fill_table_nonKegg(self, mainWindow, results_list):
69
+ try:
70
+ self.tableWidget.clearContents()
71
+ self.mainWindow = mainWindow
72
+ self.tableWidget.setColumnCount(5)
73
+ self.mainWindow.progressBar.setValue(85)
74
+ self.tableWidget.setHorizontalHeaderLabels(["Feature Type", "Chromosome/Scaffold #", "Feature ID/Locus Tag", "Feature Name", "Feature Description"])
75
+
76
+ mainWindow.checkBoxes = []
77
+ self.type = "nonkegg"
78
+
79
+ for index, result in enumerate(results_list[:1000]): # Limit to first 1000 results
80
+ self.tableWidget.setRowCount(index + 1) # Increment table row count
81
+ chrom, feature = result
82
+
83
+ # Create and set table items
84
+ items = [
85
+ QtWidgets.QTableWidgetItem(feature.type),
86
+ QtWidgets.QTableWidgetItem(str(chrom)),
87
+ QtWidgets.QTableWidgetItem(get_id(feature)),
88
+ QtWidgets.QTableWidgetItem(get_name(feature)),
89
+ QtWidgets.QTableWidgetItem(get_description(feature))
90
+ ]
91
+
92
+ for col, item in enumerate(items):
93
+ self.tableWidget.setItem(index, col, item)
94
+
95
+ mainWindow.checkBoxes.append((chrom, feature, index))
96
+
97
+ self.tableWidget.resizeColumnsToContents()
98
+ mainWindow.hide()
99
+
100
+ #center on current screen
101
+ center_ui(self)
102
+ self.show()
103
+ self.activateWindow()
104
+
105
+ return 0
106
+ except Exception as e:
107
+ show_error("fill_table_nonKegg() in AnnotationWindow", e)
108
+
109
+ # this is the connection for the select all checkbox - selects/deselects all the genes in the table
110
+ def select_all_genes(self):
111
+ try:
112
+ if self.select_all_checkbox.isChecked():
113
+ self.tableWidget.selectAll()
114
+ else:
115
+ self.tableWidget.clearSelection()
116
+ except Exception as e:
117
+ show_error("select_all_genes() in AnnotationWindow", e)
118
+
119
+ # this function calls the closingWindow class.
120
+ def closeEvent(self, event):
121
+ try:
122
+ GlobalSettings.mainWindow.closeFunction()
123
+ except Exception as e:
124
+ show_error("closeEvent() in AnnotationWindow", e)
125
+ event.accept()
main.py → views/CMainWindow.py RENAMED
@@ -1,170 +1,35 @@
1
- from Algorithms import get_table_headers
2
- import sys
3
- import os
4
- import io
5
- from PyQt5 import QtWidgets, Qt, QtGui, QtCore, uic
6
- from CoTargeting import CoTargeting
7
- from closingWin import closingWindow
8
- from Results import Results
9
- from NewGenome import NewGenome
10
- from NewEndonuclease import NewEndonuclease
11
- import genomeBrowser
12
- import webbrowser
13
- import requests
14
- import GlobalSettings
15
- import multitargeting
16
- from AnnotationParser import Annotation_Parser
17
- from export_tool import export_tool
18
- from generateLib import genLibrary
19
- from CSPRparser import CSPRparser
20
- import populationAnalysis
21
  import platform
22
- import ncbi
 
 
 
23
  import glob
24
  import traceback
25
- import math
26
- import logging
27
- from annotation_functions import *
28
- from error_handling import show_error
29
- from common_utils import show_message
30
- from ui_utils import scale_ui, center_ui
31
-
32
- #logger alias for global logger
 
 
 
 
 
 
 
 
 
33
  logger = GlobalSettings.logger
34
 
35
  fontSize = 12
36
 
37
- #Annotation file and search query from MainWindow
38
- class AnnotationsWindow(QtWidgets.QMainWindow):
39
- #init annotation window class
40
- def __init__(self, info_path):
41
- super(AnnotationsWindow, self).__init__()
42
- uic.loadUi(GlobalSettings.appdir + 'annotation_details.ui', self)
43
- self.setWindowIcon(Qt.QIcon(GlobalSettings.appdir + "cas9image.ico"))
44
- self.Submit_button.clicked.connect(self.submit)
45
- self.Go_Back_Button.clicked.connect(self.go_Back)
46
- self.select_all_checkbox.stateChanged.connect(self.select_all_genes)
47
- self.fontSize = 12 # Initialize fontSize
48
- self.mainWindow = ""
49
- self.type = ""
50
- self.mwfg = self.frameGeometry() ##Center window
51
- self.cp = QtWidgets.QDesktopWidget().availableGeometry().center() ##Center window
52
- self.switcher_table = [1, 1, 1, 1, 1, 1, 1, 1]
53
- self.tableWidget.setHorizontalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
54
- self.tableWidget.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel)
55
- self.tableWidget.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
56
- self.tableWidget.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
57
- self.tableWidget.setAutoScroll(False)
58
- self.tableWidget.horizontalHeader().sectionClicked.connect(self.table_sorting)
59
-
60
- scale_ui(self)
61
-
62
-
63
- def table_sorting(self, logicalIndex):
64
- try:
65
- self.switcher_table[logicalIndex] *= -1
66
- order = QtCore.Qt.DescendingOrder if self.switcher_table[logicalIndex] == -1 else QtCore.Qt.AscendingOrder
67
- self.tableWidget.sortItems(logicalIndex, order)
68
- except Exception as e:
69
- show_error("table_sorting() in Annotation Window", e)
70
-
71
- #submit selected rows for results to process
72
- def submit(self):
73
- try:
74
- self.mainWindow.collect_table_data_nonkegg()
75
- self.mainWindow.show() # Open main window back up
76
- except Exception as e:
77
- logger.critical("Error in submit() in AnnotationsWindow.")
78
- logger.critical(e)
79
- logger.critical(traceback.format_exc())
80
- show_error("submit() in AnnotationsWindow", e)
81
- finally:
82
- self.hide() # Close annotation window regardless of success or failure
83
-
84
- #go back to main
85
- def go_Back(self):
86
- try:
87
- self.tableWidget.clear()
88
- self.mainWindow.searches.clear()
89
- self.tableWidget.setColumnCount(0)
90
- self.mainWindow.show()
91
- self.mainWindow.progressBar.setValue(0)
92
- self.hide()
93
- except Exception as e:
94
- show_error("go_Back() in AnnotationsWindow", e)
95
- self.mainWindow.checkBoxes.clear()
96
-
97
- # this is the connection for the select all checkbox - selects/deselects all the genes in the table
98
- # this function is very similar to the other fill_table, it just works with the other types of annotation files
99
- def fill_table_nonKegg(self, mainWindow, results_list):
100
- try:
101
- self.tableWidget.clearContents()
102
- self.mainWindow = mainWindow
103
- self.tableWidget.setColumnCount(5)
104
- self.mainWindow.progressBar.setValue(85)
105
- self.tableWidget.setHorizontalHeaderLabels(["Feature Type", "Chromosome/Scaffold #", "Feature ID/Locus Tag", "Feature Name", "Feature Description"])
106
-
107
- mainWindow.checkBoxes = []
108
- self.type = "nonkegg"
109
-
110
- for index, result in enumerate(results_list[:1000]): # Limit to first 1000 results
111
- self.tableWidget.setRowCount(index + 1) # Increment table row count
112
- chrom, feature = result
113
-
114
- # Create and set table items
115
- items = [
116
- QtWidgets.QTableWidgetItem(feature.type),
117
- QtWidgets.QTableWidgetItem(str(chrom)),
118
- QtWidgets.QTableWidgetItem(get_id(feature)),
119
- QtWidgets.QTableWidgetItem(get_name(feature)),
120
- QtWidgets.QTableWidgetItem(get_description(feature))
121
- ]
122
-
123
- for col, item in enumerate(items):
124
- self.tableWidget.setItem(index, col, item)
125
-
126
- mainWindow.checkBoxes.append((chrom, feature, index))
127
-
128
- self.tableWidget.resizeColumnsToContents()
129
- mainWindow.hide()
130
-
131
- #center on current screen
132
- center_ui(self)
133
- self.show()
134
- self.activateWindow()
135
-
136
- return 0
137
- except Exception as e:
138
- show_error("fill_table_nonKegg() in AnnotationsWindow", e)
139
-
140
- # this is the connection for the select all checkbox - selects/deselects all the genes in the table
141
- def select_all_genes(self):
142
- try:
143
- if self.select_all_checkbox.isChecked():
144
- self.tableWidget.selectAll()
145
- else:
146
- self.tableWidget.clearSelection()
147
- except Exception as e:
148
- show_error("select_all_genes() in AnnotationsWindow", e)
149
-
150
- # this function calls the closingWindow class.
151
- def closeEvent(self, event):
152
- try:
153
- GlobalSettings.mainWindow.closeFunction()
154
- except Exception as e:
155
- show_error("closeEvent() in AnnotationsWindow", e)
156
- event.accept()
157
-
158
- # =========================================================================================
159
- # CLASS NAME: CMainWindow
160
- # Inputs: Takes in the path information from the startup window and also all input parameters
161
- # Outputs: The results of the target search process by generating a new Results window
162
- # =========================================================================================
163
  class CMainWindow(QtWidgets.QMainWindow):
164
-
165
  def __init__(self, info_path):
166
  super(CMainWindow, self).__init__()
167
- uic.loadUi(GlobalSettings.appdir + 'CASPER_main.ui', self)
168
  self.dbpath = ""
169
  self.inputstring = "" # This is the search string
170
  self.info_path = info_path
@@ -220,18 +85,18 @@ class CMainWindow(QtWidgets.QMainWindow):
220
  """ Connect functions to actions (menu bar) """
221
  self.actionOpen_Genome_Browser.triggered.connect(self.launch_newGenomeBrowser)
222
  self.actionExit.triggered.connect(self.close_app)
223
- self.visit_repo.triggered.connect(self.visit_repo_func)
224
  self.actionChange_Directory.triggered.connect(self.change_directory)
225
- self.actionNCBI.triggered.connect(self.open_ncbi_web_page)
226
  # self.actionCasper2.triggered.connect(self.open_casper2_web_page)
227
- self.actionNCBI_BLAST.triggered.connect(self.open_ncbi_blast_web_page)
228
 
229
 
230
 
231
  self.progressBar.setMinimum(0)
232
  self.progressBar.setMaximum(100)
233
  self.progressBar.reset()
234
- self.Annotation_Window = AnnotationsWindow(info_path)
235
  self.geneEntryField.setPlaceholderText("Example Inputs: \n\n"
236
  "Option 1: Feature (ID, Locus Tag, or Name)\n"
237
  "Example: 854068/YOL086C/ADH1 for S. cerevisiae alcohol dehydrogenase 1\n\n"
@@ -256,9 +121,8 @@ class CMainWindow(QtWidgets.QMainWindow):
256
  self.genomebrowser = genomeBrowser.genomebrowser()
257
  self.launch_ncbi_button.clicked.connect(self.launch_ncbi)
258
 
259
- #scale UI
260
  self.first_show = True
261
- scale_ui(self)
262
 
263
  # this function prepares everything for the generate library function
264
  # it is very similar to the gather settings, how ever it stores the data instead of calling the Annotation Window class
@@ -341,21 +205,8 @@ class CMainWindow(QtWidgets.QMainWindow):
341
  else:
342
  self.progressBar.setValue(0)
343
  except Exception as e:
344
- logger.critical("Error in prep_genlib() in main.")
345
- logger.critical(e)
346
- logger.critical(traceback.format_exc())
347
-
348
- #print("prep genlib")
349
- msgBox = QtWidgets.QMessageBox()
350
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
351
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
352
- msgBox.setWindowTitle("Fatal Error")
353
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
354
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
355
- msgBox.exec()
356
-
357
- exit(-1)
358
-
359
  # Function for collecting the settings from the input field and transferring them to run_results
360
  def gather_settings(self):
361
  try:
@@ -405,26 +256,8 @@ class CMainWindow(QtWidgets.QMainWindow):
405
  sinput = self.inputstring
406
  self.run_results("sequence", sinput, same_search)
407
  except Exception as e:
408
- logger.critical("Error in gather_settings() in main.")
409
- logger.critical(e)
410
- logger.critical(traceback.format_exc())
411
- msgBox = QtWidgets.QMessageBox()
412
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
413
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
414
- msgBox.setWindowTitle("Fatal Error")
415
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
416
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
417
- msgBox.exec()
418
- msgBox = QtWidgets.QMessageBox()
419
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
420
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
421
- msgBox.setWindowTitle("Fatal Error")
422
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
423
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
424
- msgBox.exec()
425
-
426
- exit(-1)
427
-
428
  # ---- Following functions are for running the auxillary algorithms and windows ---- #
429
  # this function is parses the annotation file given, and then goes through and goes onto results
430
  # it will call other versions of collect_table_data and fill_table that work with these file types
@@ -746,19 +579,8 @@ class CMainWindow(QtWidgets.QMainWindow):
746
 
747
 
748
  except Exception as e:
749
- logger.critical("Error in run_results() in main.")
750
- logger.critical(e)
751
- logger.critical(traceback.format_exc())
752
- msgBox = QtWidgets.QMessageBox()
753
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
754
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
755
- msgBox.setWindowTitle("Fatal Error")
756
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
757
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
758
- msgBox.exec()
759
-
760
- exit(-1)
761
-
762
  def handle_feature_search(self, input_string, open_anno_window):
763
  file_type = self.annotation_parser.find_which_file_version()
764
  if file_type == -1 or self.annotation_files.currentText() == "None":
@@ -778,26 +600,23 @@ class CMainWindow(QtWidgets.QMainWindow):
778
  self.hide()
779
  self.newGenome.show()
780
  except Exception as e:
781
- show_error("launch_newGenome() in main", e)
782
 
783
- #launch new endo tool
784
  def launch_newEndonuclease(self):
785
  try:
786
- # Initialize and display the new endonuclease window
787
- self.newEndonuclease.centerUI()
788
  self.newEndonuclease.show()
789
  self.newEndonuclease.activateWindow()
790
  except Exception as e:
791
- show_error("launch_newEndonuclease() in main", e)
792
 
793
  #launch genome browser tool
794
  def launch_newGenomeBrowser(self):
795
  try:
796
  self.genomebrowser.createGraph(self)
797
  except Exception as e:
798
- show_error("launch_newGenomeBrowser() in main", e)
799
 
800
- #launch ncbi tool
801
  def launch_ncbi(self):
802
  try:
803
  msgBox = QtWidgets.QMessageBox()
@@ -811,7 +630,7 @@ class CMainWindow(QtWidgets.QMainWindow):
811
 
812
  if self.ncbi.first_show:
813
  self.ncbi.first_show = False
814
- self.ncbi.centerUI()
815
 
816
  self.ncbi.show()
817
  self.ncbi.activateWindow()
@@ -859,19 +678,8 @@ class CMainWindow(QtWidgets.QMainWindow):
859
  self.pushButton_ViewTargets.setEnabled(True)
860
  self.GenerateLibrary.setEnabled(True)
861
  except Exception as e:
862
- logger.critical("Error in collect_table_data_nonkegg() in main.")
863
- logger.critical(e)
864
- logger.critical(traceback.format_exc())
865
- msgBox = QtWidgets.QMessageBox()
866
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
867
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
868
- msgBox.setWindowTitle("Fatal Error")
869
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
870
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
871
- msgBox.exec()
872
-
873
- exit(-1)
874
-
875
  def separate_line(self, input_string):
876
  try:
877
  export_array = []
@@ -886,19 +694,8 @@ class CMainWindow(QtWidgets.QMainWindow):
886
  export_array.append(input_string[:index])
887
  input_string = input_string[index + 1:]
888
  except Exception as e:
889
- logger.critical("Error in seperate_line() in main.")
890
- logger.critical(e)
891
- logger.critical(traceback.format_exc())
892
- msgBox = QtWidgets.QMessageBox()
893
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
894
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
895
- msgBox.setWindowTitle("Fatal Error")
896
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
897
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
898
- msgBox.exec()
899
-
900
- exit(-1)
901
-
902
  def removeWhiteSpace(self, strng):
903
  try:
904
  while True:
@@ -910,18 +707,7 @@ class CMainWindow(QtWidgets.QMainWindow):
910
  return strng
911
  strng = strng[:len(strng) - 1]
912
  except Exception as e:
913
- logger.critical("Error in removeWhiteSpace() in main.")
914
- logger.critical(e)
915
- logger.critical(traceback.format_exc())
916
- msgBox = QtWidgets.QMessageBox()
917
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
918
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
919
- msgBox.setWindowTitle("Fatal Error")
920
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
921
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
922
- msgBox.exec()
923
-
924
- exit(-1)
925
 
926
  # Function to enable and disable the Annotation function if searching by position or sequence
927
  def toggle_annotation(self):
@@ -931,18 +717,7 @@ class CMainWindow(QtWidgets.QMainWindow):
931
  else:
932
  self.Step2.setEnabled(True)
933
  except Exception as e:
934
- logger.critical("Error in toggle_annotation() in main.")
935
- logger.critical(e)
936
- logger.critical(traceback.format_exc())
937
- msgBox = QtWidgets.QMessageBox()
938
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
939
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
940
- msgBox.setWindowTitle("Fatal Error")
941
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
942
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
943
- msgBox.exec()
944
-
945
- exit(-1)
946
 
947
  def fill_annotation_dropdown(self):
948
  try:
@@ -962,19 +737,8 @@ class CMainWindow(QtWidgets.QMainWindow):
962
  self.annotation_files.addItems(annotation_files)
963
  self.annotation_files.addItems(["None"])
964
  except Exception as e:
965
- logger.critical("Error in fill_annotation_dropdown() in main.")
966
- logger.critical(e)
967
- logger.critical(traceback.format_exc())
968
- msgBox = QtWidgets.QMessageBox()
969
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
970
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
971
- msgBox.setWindowTitle("Fatal Error")
972
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
973
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
974
- msgBox.exec()
975
-
976
- exit(-1)
977
-
978
  def make_dictonary(self):
979
  try:
980
  url = "https://www.genome.jp/dbget-bin/get_linkdb?-t+genes+gn:" + self.TNumbers[
@@ -1009,18 +773,7 @@ class CMainWindow(QtWidgets.QMainWindow):
1009
  self.gene_list[key] = [seq]
1010
  z = 5
1011
  except Exception as e:
1012
- logger.critical("Error in make_dictionary() in main.")
1013
- logger.critical(e)
1014
- logger.critical(traceback.format_exc())
1015
- msgBox = QtWidgets.QMessageBox()
1016
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1017
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1018
- msgBox.setWindowTitle("Fatal Error")
1019
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1020
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1021
- msgBox.exec()
1022
-
1023
- exit(-1)
1024
 
1025
  def organism_finder(self, long_str):
1026
  try:
@@ -1032,18 +785,7 @@ class CMainWindow(QtWidgets.QMainWindow):
1032
  index = index + 1
1033
  return long_str[:semi - index]
1034
  except Exception as e:
1035
- logger.critical("Error trying in organism_finder() in main.")
1036
- logger.critical(e)
1037
- logger.critical(traceback.format_exc())
1038
- msgBox = QtWidgets.QMessageBox()
1039
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1040
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1041
- msgBox.setWindowTitle("Fatal Error")
1042
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1043
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1044
- msgBox.exec()
1045
-
1046
- exit(-1)
1047
 
1048
  # This method is for testing the execution of a button call to make sure the button is linked properly
1049
  def testexe(self):
@@ -1064,19 +806,8 @@ class CMainWindow(QtWidgets.QMainWindow):
1064
  else:
1065
  pass
1066
  except Exception as e:
1067
- logger.critical("Error in testexe() in main.")
1068
- logger.critical(e)
1069
- logger.critical(traceback.format_exc())
1070
- msgBox = QtWidgets.QMessageBox()
1071
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1072
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1073
- msgBox.setWindowTitle("Fatal Error")
1074
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1075
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1076
- msgBox.exec()
1077
-
1078
- exit(-1)
1079
-
1080
  def getData(self):
1081
  try:
1082
  try:
@@ -1129,18 +860,7 @@ class CMainWindow(QtWidgets.QMainWindow):
1129
  self.endoChoice.addItems(self.organisms_to_endos[str(self.orgChoice.currentText())])
1130
  self.orgChoice.currentIndexChanged.connect(self.changeEndos)
1131
  except Exception as e:
1132
- logger.critical("Error in getData() in main.")
1133
- logger.critical(e)
1134
- logger.critical(traceback.format_exc())
1135
- msgBox = QtWidgets.QMessageBox()
1136
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1137
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1138
- msgBox.setWindowTitle("Fatal Error")
1139
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1140
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1141
- msgBox.exec()
1142
-
1143
- exit(-1)
1144
 
1145
  def changeEndos(self):
1146
  try:
@@ -1158,18 +878,7 @@ class CMainWindow(QtWidgets.QMainWindow):
1158
  self.radioButton_Gene.hide()
1159
  self.radioButton_Position.hide()
1160
  except Exception as e:
1161
- logger.critical("Error in changeEndos() in main.")
1162
- logger.critical(e)
1163
- logger.critical(traceback.format_exc())
1164
- msgBox = QtWidgets.QMessageBox()
1165
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1166
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1167
- msgBox.setWindowTitle("Fatal Error")
1168
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1169
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1170
- msgBox.exec()
1171
-
1172
- exit(-1)
1173
 
1174
  def change_directory(self):
1175
  try:
@@ -1206,7 +915,6 @@ class CMainWindow(QtWidgets.QMainWindow):
1206
  except Exception as e:
1207
  show_error("Error in change_directory() in main.", e)
1208
 
1209
- #change to multi-targeting window
1210
  def changeto_multitargeting(self):
1211
  try:
1212
  os.chdir(os.getcwd())
@@ -1218,41 +926,19 @@ class CMainWindow(QtWidgets.QMainWindow):
1218
  GlobalSettings.mainWindow.hide()
1219
 
1220
  except Exception as e:
1221
- logger.critical("Error in changeto_multitargeting() in main.")
1222
- logger.critical(e)
1223
- logger.critical(traceback.format_exc())
1224
- msgBox = QtWidgets.QMessageBox()
1225
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1226
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1227
- msgBox.setWindowTitle("Fatal Error")
1228
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1229
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1230
- msgBox.exec()
1231
-
1232
- exit(-1)
1233
 
1234
  #change to population analysis window
1235
  def changeto_population_Analysis(self):
1236
  try:
1237
  GlobalSettings.pop_Analysis.launch()
1238
  if GlobalSettings.pop_Analysis.first_show == True:
1239
- GlobalSettings.pop_Analysis.centerUI()
1240
  GlobalSettings.pop_Analysis.first_show = False
1241
  GlobalSettings.pop_Analysis.show()
1242
  GlobalSettings.mainWindow.hide()
1243
  except Exception as e:
1244
- logger.critical("Error in changeto_population_Analysis() in main.")
1245
- logger.critical(e)
1246
- logger.critical(traceback.format_exc())
1247
- msgBox = QtWidgets.QMessageBox()
1248
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1249
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1250
- msgBox.setWindowTitle("Fatal Error")
1251
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1252
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1253
- msgBox.exec()
1254
-
1255
- exit(-1)
1256
 
1257
  def annotation_information(self):
1258
  try:
@@ -1268,46 +954,8 @@ class CMainWindow(QtWidgets.QMainWindow):
1268
  msgBox.exec()
1269
 
1270
  except Exception as e:
1271
- logger.critical("Error in annotation_information() in main.")
1272
- logger.critical(e)
1273
- logger.critical(traceback.format_exc())
1274
- msgBox = QtWidgets.QMessageBox()
1275
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1276
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1277
- msgBox.setWindowTitle("Fatal Error")
1278
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1279
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1280
- msgBox.exec()
1281
-
1282
- exit(-1)
1283
-
1284
- def open_ncbi_blast_web_page(self):
1285
- try:
1286
- webbrowser.open('https://blast.ncbi.nlm.nih.gov/Blast.cgi', new=2)
1287
- except Exception as e:
1288
- show_error("open_ncbi_blast_web_page() in main", e)
1289
-
1290
- def open_ncbi_web_page(self):
1291
- try:
1292
- webbrowser.open('https://www.ncbi.nlm.nih.gov/', new=2)
1293
- except Exception as e:
1294
- show_error("open_ncbi_web_page() in main", e)
1295
-
1296
- # def open_casper2_web_page(self):
1297
- # try:
1298
- # webbrowser.open('http://casper2.org/', new=2)
1299
- # except Exception as e:
1300
- # logger.critical("Error in open_casper2_web_page() in main.")
1301
- # logger.critical(e)
1302
- # logger.critical(traceback.format_exc())
1303
- # exit(-1)
1304
-
1305
- def visit_repo_func(self):
1306
- try:
1307
- webbrowser.open('https://github.com/TrinhLab/CASPERapp')
1308
- except Exception as e:
1309
- show_error("visit_repo_func() in main", e)
1310
-
1311
  @QtCore.pyqtSlot()
1312
  def view_results(self):
1313
  try:
@@ -1319,18 +967,9 @@ class CMainWindow(QtWidgets.QMainWindow):
1319
  self.Results.show()
1320
  self.hide()
1321
  except Exception as e:
1322
- logger.critical("Error in view_results() in main.")
1323
- logger.critical(e)
1324
- logger.critical(traceback.format_exc())
1325
- msgBox = QtWidgets.QMessageBox()
1326
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1327
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1328
- msgBox.setWindowTitle("Fatal Error")
1329
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1330
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1331
- msgBox.exec()
1332
 
1333
- exit(-1)
1334
 
1335
  def closeFunction(self):
1336
  try:
@@ -1340,12 +979,11 @@ class CMainWindow(QtWidgets.QMainWindow):
1340
  except AttributeError:
1341
  print("No NCBI window to close.")
1342
 
1343
- # Proceed with closing operations
1344
  self.myClosingWindow.get_files()
1345
- self.myClosingWindow.centerUI()
1346
  self.myClosingWindow.show()
1347
  except Exception as e:
1348
- show_error("closeFunction() in main", e)
1349
 
1350
  def close_app(self):
1351
  try:
@@ -1359,360 +997,4 @@ class CMainWindow(QtWidgets.QMainWindow):
1359
  self.closeFunction()
1360
  self.close()
1361
  except Exception as e:
1362
- show_error("close_app() in main", e)
1363
-
1364
- #startup window class
1365
- class StartupWindow(QtWidgets.QMainWindow):
1366
- def __init__(self):
1367
- try:
1368
- super(StartupWindow, self).__init__()
1369
-
1370
- #load UX files
1371
- try:
1372
- uic.loadUi(GlobalSettings.appdir + 'startupCASPER.ui', self)
1373
- self.setWindowIcon(QtGui.QIcon(GlobalSettings.appdir + "cas9image.png"))
1374
- except Exception as e:
1375
- logger.critical("Unable to load UX files for Startup Window.")
1376
- logger.critical(e)
1377
- logger.critical(traceback.format_exc())
1378
- exit(-1)
1379
-
1380
- #set "Main" button to be the default highlighted button on startup
1381
- self.goToMain.setDefault(True)
1382
-
1383
- #get current directory, and update based on current operating system
1384
- self.currentDirectory = os.getcwd()
1385
- self.databaseDirectory = self.loadDatabaseDirectory()
1386
- GlobalSettings.CSPR_DB = self.databaseDirectory
1387
- if platform.system() == "Windows":
1388
- GlobalSettings.CSPR_DB = GlobalSettings.CSPR_DB.replace("/","\\")
1389
- else:
1390
- GlobalSettings.CSPR_DB = GlobalSettings.CSPR_DB.replace("\\","/")
1391
-
1392
- #setup event handlers for startup buttons
1393
- self.currentDirText.setText(self.databaseDirectory)
1394
- self.changeDir.clicked.connect(self.change_directory)
1395
- self.goToMain.clicked.connect(self.launchMainWindow)
1396
- self.goToNewGenome.clicked.connect(self.launchNewGenome)
1397
-
1398
- self.setWindowTitle("CASPER")
1399
- self.setWindowIcon(Qt.QIcon(GlobalSettings.appdir + "cas9image.ico"))
1400
-
1401
- #scale UI
1402
- scale_ui(self)
1403
-
1404
- except Exception as e:
1405
- logger.critical("Error initializing StartupWindow class.")
1406
- logger.critical(e)
1407
- logger.critical(traceback.format_exc())
1408
- exit(-1)
1409
-
1410
- #event handler for user clicking the "Change..." button - used for changing CASPER database directory
1411
- def change_directory(self):
1412
- try:
1413
- # Launch OS file browser
1414
- newDirectory = QtWidgets.QFileDialog.getExistingDirectory(
1415
- self, "Open a folder...", self.databaseDirectory, QtWidgets.QFileDialog.ShowDirsOnly)
1416
-
1417
- # Check if selected path is a directory in the system
1418
- if not os.path.isdir(newDirectory):
1419
- show_message(
1420
- fontSize=self.fontSize,
1421
- icon=QtWidgets.QMessageBox.Icon.Critical,
1422
- title="Not a directory",
1423
- message="The directory you selected does not exist.",
1424
- )
1425
- return
1426
-
1427
- # Ensure directory contains correct filepath format based on OS
1428
- newDirectory = newDirectory.replace("/", "\\") if platform.system() == "Windows" else newDirectory.replace("\\", "/")
1429
-
1430
- # Update text edit showing the current selected database directory
1431
- self.currentDirText.setText(newDirectory)
1432
-
1433
- # Update CASPER database directories
1434
- self.databaseDirectory = newDirectory
1435
- GlobalSettings.CSPR_DB = newDirectory
1436
-
1437
- except Exception as e:
1438
- show_error("change_directory() in startup window", e)
1439
-
1440
- #function for loading the default database directory specified in CASPERinfo
1441
- #returns: default database parsed from CASPERinfo
1442
- def loadDatabaseDirectory(self):
1443
- casperInfoPath = os.path.join(GlobalSettings.appdir, "CASPERinfo")
1444
- defaultDirectory = "Where would you like to store CASPER database files?" # Default message if directory not found
1445
-
1446
- try:
1447
- with open(casperInfoPath, 'r') as file:
1448
- for line in file:
1449
- if 'DIRECTORY:' in line:
1450
- defaultDirectory = line.strip().replace("DIRECTORY:", "").strip()
1451
- break
1452
-
1453
- # Ensure the directory path is formatted correctly based on the operating system
1454
- if platform.system() == "Windows":
1455
- defaultDirectory = defaultDirectory.replace("/", "\\")
1456
- else:
1457
- defaultDirectory = defaultDirectory.replace("\\", "/")
1458
-
1459
- logger.debug("Successfully parsed CASPERinfo for default database directory.")
1460
- return defaultDirectory
1461
-
1462
- except Exception as e:
1463
- logger.error(f"Error reading {casperInfoPath}: {e}")
1464
- logger.error(traceback.format_exc())
1465
-
1466
- return defaultDirectory
1467
-
1468
- #function for saving the currently selected database directory to CASPERinfo to be the new default value on startup
1469
- def saveDatabaseDirectory(self):
1470
- try:
1471
- #variable to hold the CASPERinfo data with new default directory change
1472
- CASPERInfoNewData = ""
1473
-
1474
- #new default directory string for CASPERinfo
1475
- newDefaultDirectory = "DIRECTORY:" + str(self.databaseDirectory)
1476
-
1477
- #open CASPERinfo file to read in the files data and add in new change
1478
- try:
1479
- CASPERInfo = open(GlobalSettings.appdir + "CASPERinfo", 'r+')
1480
- CASPERinfoData = CASPERInfo.read()
1481
- CASPERinfoData = CASPERinfoData.split('\n')
1482
- for line in CASPERinfoData:
1483
- #if directory line found, use new default directory string instead
1484
- if 'DIRECTORY:' in line:
1485
- CASPERInfoNewData = CASPERInfoNewData + "\n" + newDefaultDirectory
1486
- else:
1487
- CASPERInfoNewData = CASPERInfoNewData + "\n" + line
1488
- CASPERInfoNewData = CASPERInfoNewData[1:]
1489
-
1490
- #close CASPERinfo
1491
- CASPERInfo.close()
1492
-
1493
- #re-open the file and re-write it with current changes
1494
- CASPERInfo = open(GlobalSettings.appdir + "CASPERinfo", 'w+')
1495
- CASPERInfo.write(CASPERInfoNewData)
1496
- CASPERInfo.close()
1497
- logger.debug("Successfully updated CASPERinfo with new default database directory.")
1498
- except Exception as e:
1499
- logger.critical("Unable to write to CASPERinfo file to update database directory.")
1500
- logger.critical(e)
1501
- logger.critical(traceback.format_exc())
1502
- msgBox = QtWidgets.QMessageBox()
1503
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1504
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1505
- msgBox.setWindowTitle("Fatal Error")
1506
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1507
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1508
- msgBox.exec()
1509
-
1510
- exit(-1)
1511
- except Exception as e:
1512
- logger.critical("Error in saveDatabaseDirectory() in startup window.")
1513
- logger.critical(e)
1514
- logger.critical(traceback.format_exc())
1515
- msgBox = QtWidgets.QMessageBox()
1516
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1517
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1518
- msgBox.setWindowTitle("Fatal Error")
1519
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1520
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1521
- msgBox.exec()
1522
-
1523
- exit(-1)
1524
-
1525
- # Event handler for user clicking the "New Genome" button - used for launching New Genome
1526
- def launchNewGenome(self):
1527
- try:
1528
- # Make sure database directory variable is up-to-date based on what the user has in the text edit
1529
- self.databaseDirectory = str(self.currentDirText.text())
1530
-
1531
- if not os.path.isdir(self.databaseDirectory):
1532
- show_message(
1533
- fontSize=12,
1534
- icon=QtWidgets.QMessageBox.Icon.Critical,
1535
- title="Not a directory",
1536
- message="The directory you selected does not exist.",
1537
- )
1538
- return
1539
-
1540
- # Change directories to the specified database directory provided
1541
- os.chdir(self.databaseDirectory)
1542
-
1543
- # Write out the database directory to CASPERinfo to be the new default loaded value
1544
- self.saveDatabaseDirectory()
1545
-
1546
- # Update global database variable
1547
- GlobalSettings.CSPR_DB = self.databaseDirectory
1548
-
1549
- # Create app directories
1550
- initialize_app_directories()
1551
-
1552
- # Launch New Genome window
1553
- self.launch_new_genome()
1554
-
1555
- self.close()
1556
- except Exception as e:
1557
- show_error("launchNewGenome() in startup window", e)
1558
-
1559
- def launch_new_genome(self):
1560
- try:
1561
- GlobalSettings.mainWindow.launch_newGenome()
1562
- logger.debug("Successfully initialized New Genome in startup window.")
1563
- except Exception as e:
1564
- show_error("launch_new_genome() in startup window", e)
1565
-
1566
- # Event handler for user clicking "Main Program" button - used to launch Main Window
1567
- def launchMainWindow(self):
1568
- try:
1569
- # Make sure database directory variable is up-to-date based on what the user has in the text edit
1570
- self.databaseDirectory = str(self.currentDirText.text())
1571
-
1572
- # Make sure the path is a valid path before launching New Genome
1573
- if not os.path.isdir(self.databaseDirectory):
1574
- show_message(
1575
- fontSize=12,
1576
- icon=QtWidgets.QMessageBox.Icon.Critical,
1577
- title="Not a directory",
1578
- message="The directory you selected does not exist.",
1579
- )
1580
- return
1581
-
1582
- # Check if database directory has CSPR files in it
1583
- if not any(file.endswith(".cspr") for file in os.listdir(self.databaseDirectory)):
1584
- show_message(
1585
- fontSize=12,
1586
- icon=QtWidgets.QMessageBox.Icon.Critical,
1587
- title="Directory is invalid!",
1588
- message="You must select a directory with CSPR Files!",
1589
- )
1590
- return
1591
-
1592
- # Change directory to database directory
1593
- os.chdir(self.databaseDirectory)
1594
-
1595
- # Update database directory global variable
1596
- GlobalSettings.CSPR_DB = self.databaseDirectory
1597
-
1598
- # Save database directory to CASPERinfo
1599
- self.saveDatabaseDirectory()
1600
-
1601
- initialize_app_directories()
1602
-
1603
- # Fill in organism/endo/annotation dropdown information for main, mulit-targeting, and populatin analysis
1604
- self.load_dropdown_data()
1605
-
1606
- # Show main window
1607
- if GlobalSettings.mainWindow.first_show:
1608
- GlobalSettings.mainWindow.first_show = False
1609
- GlobalSettings.mainWindow.show()
1610
- self.close()
1611
-
1612
- except Exception as e:
1613
- show_error("launchMainWindow() in startup window", e)
1614
-
1615
- def load_dropdown_data(self):
1616
- try:
1617
- GlobalSettings.mainWindow.getData()
1618
- GlobalSettings.mainWindow.fill_annotation_dropdown()
1619
- logger.debug("Successfully loaded organism/endo/annotation drop down information in Main.")
1620
- except Exception as e:
1621
- show_error("load_dropdown_data() in Main", e)
1622
-
1623
- try:
1624
- GlobalSettings.MTWin.launch()
1625
- logger.debug("Successfully loaded organism/endo drop down information in Multi-targeting.")
1626
- except Exception as e:
1627
- show_error("load_dropdown_data() in Multi-targeting", e)
1628
-
1629
- try:
1630
- GlobalSettings.pop_Analysis.launch()
1631
- logger.debug("Successfully loaded organism/endo drop down information in Population Analysis.")
1632
- except Exception as e:
1633
- show_error("load_dropdown_data() in Population Analysis", e)
1634
-
1635
- def initialize_app_directories():
1636
- required_dirs = ["FNA", "GBFF"]
1637
- for directory in required_dirs:
1638
- path = os.path.join(GlobalSettings.CSPR_DB, directory)
1639
- if not os.path.exists(path):
1640
- os.makedirs(path, exist_ok=True)
1641
- logging.info(f"Directory created: {path}")
1642
-
1643
- def setup_logger():
1644
- logger.info(f"System OS: {platform.system()}")
1645
-
1646
- if hasattr(sys, 'frozen'):
1647
- #log CASPER is in packaged format
1648
- logger.info("Running a packaged version of CASPER.")
1649
- GlobalSettings.appdir = sys.executable
1650
- if platform.system() == 'Windows':
1651
- GlobalSettings.appdir = sys._MEIPASS + "\\"
1652
- else:
1653
- GlobalSettings.appdir = GlobalSettings.appdir[:GlobalSettings.appdir.rfind("Contents/") + 9] + "Resources/"
1654
-
1655
- else:
1656
- # log CASPER is not in packaged format
1657
- logger.info("Running a non-packaged version of CASPER.")
1658
- GlobalSettings.appdir = os.path.dirname(os.path.abspath(__file__)) + ('\\' if platform.system() == 'Windows' else '/')
1659
-
1660
- fh = logging.FileHandler(GlobalSettings.appdir + 'logs/CASPER.log', mode='w')
1661
- fh_formatter = logging.Formatter('%(asctime)s %(levelname)s %(lineno)d:%(filename)s(%(process)d) - %(message)s')
1662
- fh.setFormatter(fh_formatter)
1663
- fh.setLevel(logging.DEBUG)
1664
- GlobalSettings.logger.addHandler(fh)
1665
-
1666
- # def update_directory(current_directory, fontSize, update_ui_callback):
1667
- # try:
1668
- # filed = QtWidgets.QFileDialog()
1669
- # new_directory = QtWidgets.QFileDialog.getExistingDirectory(
1670
- # filed, "Open a folder...", current_directory, QtWidgets.QFileDialog.ShowDirsOnly)
1671
-
1672
- # if not os.path.isdir(new_directory):
1673
- # show_message("Not a directory", "The directory you selected does not exist.", fontSize)
1674
- # return
1675
-
1676
- # if not any(file.endswith(".cspr") for file in os.listdir(new_directory)):
1677
- # show_message("Directory is invalid!", "You must select a directory with CSPR Files!", fontSize)
1678
- # return
1679
-
1680
- # if platform.system() == "Windows":
1681
- # new_directory = new_directory.replace("/", "\\")
1682
-
1683
- # os.chdir(new_directory)
1684
- # GlobalSettings.CSPR_DB = new_directory
1685
- # update_ui_callback(new_directory)
1686
-
1687
- # except Exception as e:
1688
- # show_critical_error("Error in updating directory", e, fontSize)
1689
-
1690
- def main():
1691
- setup_logger()
1692
- app = QtWidgets.QApplication(sys.argv)
1693
- app.setOrganizationName("TrinhLab-UTK")
1694
- app.setApplicationName("CASPER")
1695
-
1696
- #load startup window
1697
- try:
1698
- # Initialize windows
1699
- startup = StartupWindow()
1700
- logger.debug("Successfully initialized Startup Window.")
1701
-
1702
- GlobalSettings.mainWindow = CMainWindow(os.getcwd())
1703
- logger.debug("Successfully initialized Main Window.")
1704
-
1705
- GlobalSettings.MTWin = multitargeting.Multitargeting()
1706
- logger.debug("Successfully initialized Multi-targeting Window.")
1707
-
1708
- GlobalSettings.pop_Analysis = populationAnalysis.Pop_Analysis()
1709
- logger.debug("Successfully initialized Population Analysis Window.")
1710
-
1711
- center_ui(startup)
1712
- startup.show()
1713
- sys.exit(app.exec_())
1714
- except Exception as e:
1715
- show_error("An error occurred during application initialization", e)
1716
-
1717
- if __name__ == '__main__':
1718
- main()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import platform
2
+ import controllers.ncbi as ncbi
3
+ import os
4
+ from utils.Algorithms import get_table_headers
5
+ from models.CSPRparser import CSPRparser
6
  import glob
7
  import traceback
8
+ import models.GlobalSettings as GlobalSettings
9
+ from PyQt5 import QtWidgets, QtGui, QtCore, uic, Qt
10
+ from utils.ui import scale_ui, center_ui, show_message, show_error
11
+ from views.annotation_functions import *
12
+ from views.AnnotationParser import Annotation_Parser
13
+ from views.AnnotationWindow import AnnotationWindow
14
+ import views.genomeBrowser as genomeBrowser
15
+ from views.NewGenome import NewGenome
16
+ from views.NewEndonuclease import NewEndonuclease
17
+ from controllers.CoTargeting import CoTargeting
18
+ from views.generateLib import genLibrary
19
+ import webbrowser
20
+ from controllers.Results import Results
21
+ from views.export_tool import export_tool
22
+ from views.closingWin import closingWindow
23
+ from utils.web import ncbi_page, repo_page, ncbi_blast_page
24
+
25
  logger = GlobalSettings.logger
26
 
27
  fontSize = 12
28
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  class CMainWindow(QtWidgets.QMainWindow):
 
30
  def __init__(self, info_path):
31
  super(CMainWindow, self).__init__()
32
+ uic.loadUi(GlobalSettings.appdir + 'ui/CASPER_main.ui', self)
33
  self.dbpath = ""
34
  self.inputstring = "" # This is the search string
35
  self.info_path = info_path
 
85
  """ Connect functions to actions (menu bar) """
86
  self.actionOpen_Genome_Browser.triggered.connect(self.launch_newGenomeBrowser)
87
  self.actionExit.triggered.connect(self.close_app)
88
+ self.visit_repo.triggered.connect(repo_page)
89
  self.actionChange_Directory.triggered.connect(self.change_directory)
90
+ self.actionNCBI.triggered.connect(ncbi_page)
91
  # self.actionCasper2.triggered.connect(self.open_casper2_web_page)
92
+ self.actionNCBI_BLAST.triggered.connect(ncbi_blast_page)
93
 
94
 
95
 
96
  self.progressBar.setMinimum(0)
97
  self.progressBar.setMaximum(100)
98
  self.progressBar.reset()
99
+ self.Annotation_Window = AnnotationWindow(info_path)
100
  self.geneEntryField.setPlaceholderText("Example Inputs: \n\n"
101
  "Option 1: Feature (ID, Locus Tag, or Name)\n"
102
  "Example: 854068/YOL086C/ADH1 for S. cerevisiae alcohol dehydrogenase 1\n\n"
 
121
  self.genomebrowser = genomeBrowser.genomebrowser()
122
  self.launch_ncbi_button.clicked.connect(self.launch_ncbi)
123
 
 
124
  self.first_show = True
125
+ scale_ui(self, custom_scale_width=1150, custom_scale_height=650)
126
 
127
  # this function prepares everything for the generate library function
128
  # it is very similar to the gather settings, how ever it stores the data instead of calling the Annotation Window class
 
205
  else:
206
  self.progressBar.setValue(0)
207
  except Exception as e:
208
+ show_error("Error in prep_genlib() in main", e)
209
+
 
 
 
 
 
 
 
 
 
 
 
 
 
210
  # Function for collecting the settings from the input field and transferring them to run_results
211
  def gather_settings(self):
212
  try:
 
256
  sinput = self.inputstring
257
  self.run_results("sequence", sinput, same_search)
258
  except Exception as e:
259
+ show_error("Error in gather_settings() in main", e)
260
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
261
  # ---- Following functions are for running the auxillary algorithms and windows ---- #
262
  # this function is parses the annotation file given, and then goes through and goes onto results
263
  # it will call other versions of collect_table_data and fill_table that work with these file types
 
579
 
580
 
581
  except Exception as e:
582
+ show_error("Error in run_results() in main", e)
583
+
 
 
 
 
 
 
 
 
 
 
 
584
  def handle_feature_search(self, input_string, open_anno_window):
585
  file_type = self.annotation_parser.find_which_file_version()
586
  if file_type == -1 or self.annotation_files.currentText() == "None":
 
600
  self.hide()
601
  self.newGenome.show()
602
  except Exception as e:
603
+ show_error("Error in launch_newGenome() in main", e)
604
 
 
605
  def launch_newEndonuclease(self):
606
  try:
607
+ center_ui(self.newEndonuclease)
 
608
  self.newEndonuclease.show()
609
  self.newEndonuclease.activateWindow()
610
  except Exception as e:
611
+ show_error("Error in launch_newEndonuclease() in main", e)
612
 
613
  #launch genome browser tool
614
  def launch_newGenomeBrowser(self):
615
  try:
616
  self.genomebrowser.createGraph(self)
617
  except Exception as e:
618
+ show_error("Error in launch_newGenomeBrowser() in main", e)
619
 
 
620
  def launch_ncbi(self):
621
  try:
622
  msgBox = QtWidgets.QMessageBox()
 
630
 
631
  if self.ncbi.first_show:
632
  self.ncbi.first_show = False
633
+ center_ui(self.ncbi)
634
 
635
  self.ncbi.show()
636
  self.ncbi.activateWindow()
 
678
  self.pushButton_ViewTargets.setEnabled(True)
679
  self.GenerateLibrary.setEnabled(True)
680
  except Exception as e:
681
+ show_error("Error in collect_table_data_nonkegg() in main", e)
682
+
 
 
 
 
 
 
 
 
 
 
 
683
  def separate_line(self, input_string):
684
  try:
685
  export_array = []
 
694
  export_array.append(input_string[:index])
695
  input_string = input_string[index + 1:]
696
  except Exception as e:
697
+ show_error("Error in seperate_line() in main", e)
698
+
 
 
 
 
 
 
 
 
 
 
 
699
  def removeWhiteSpace(self, strng):
700
  try:
701
  while True:
 
707
  return strng
708
  strng = strng[:len(strng) - 1]
709
  except Exception as e:
710
+ show_error("Error in removeWhiteSpace() in main", e)
 
 
 
 
 
 
 
 
 
 
 
711
 
712
  # Function to enable and disable the Annotation function if searching by position or sequence
713
  def toggle_annotation(self):
 
717
  else:
718
  self.Step2.setEnabled(True)
719
  except Exception as e:
720
+ show_error("Error in toggle_annotation() in main", e)
 
 
 
 
 
 
 
 
 
 
 
721
 
722
  def fill_annotation_dropdown(self):
723
  try:
 
737
  self.annotation_files.addItems(annotation_files)
738
  self.annotation_files.addItems(["None"])
739
  except Exception as e:
740
+ show_error("Error in fill_annotation_dropdown() in main", e)
741
+
 
 
 
 
 
 
 
 
 
 
 
742
  def make_dictonary(self):
743
  try:
744
  url = "https://www.genome.jp/dbget-bin/get_linkdb?-t+genes+gn:" + self.TNumbers[
 
773
  self.gene_list[key] = [seq]
774
  z = 5
775
  except Exception as e:
776
+ show_error("Error in make_dictionary() in main", e)
 
 
 
 
 
 
 
 
 
 
 
777
 
778
  def organism_finder(self, long_str):
779
  try:
 
785
  index = index + 1
786
  return long_str[:semi - index]
787
  except Exception as e:
788
+ show_error("Error in organism_finder() in main", e)
 
 
 
 
 
 
 
 
 
 
 
789
 
790
  # This method is for testing the execution of a button call to make sure the button is linked properly
791
  def testexe(self):
 
806
  else:
807
  pass
808
  except Exception as e:
809
+ show_error("Error in testexe() in main", e)
810
+
 
 
 
 
 
 
 
 
 
 
 
811
  def getData(self):
812
  try:
813
  try:
 
860
  self.endoChoice.addItems(self.organisms_to_endos[str(self.orgChoice.currentText())])
861
  self.orgChoice.currentIndexChanged.connect(self.changeEndos)
862
  except Exception as e:
863
+ show_error("Error in getData() in main.", e)
 
 
 
 
 
 
 
 
 
 
 
864
 
865
  def changeEndos(self):
866
  try:
 
878
  self.radioButton_Gene.hide()
879
  self.radioButton_Position.hide()
880
  except Exception as e:
881
+ show_error("Error in changeEndos() in main", e)
 
 
 
 
 
 
 
 
 
 
 
882
 
883
  def change_directory(self):
884
  try:
 
915
  except Exception as e:
916
  show_error("Error in change_directory() in main.", e)
917
 
 
918
  def changeto_multitargeting(self):
919
  try:
920
  os.chdir(os.getcwd())
 
926
  GlobalSettings.mainWindow.hide()
927
 
928
  except Exception as e:
929
+ show_error("Error in changeto_multitargeting() in main.", e)
 
 
 
 
 
 
 
 
 
 
 
930
 
931
  #change to population analysis window
932
  def changeto_population_Analysis(self):
933
  try:
934
  GlobalSettings.pop_Analysis.launch()
935
  if GlobalSettings.pop_Analysis.first_show == True:
936
+ center_ui(GlobalSettings.pop_Analysis)
937
  GlobalSettings.pop_Analysis.first_show = False
938
  GlobalSettings.pop_Analysis.show()
939
  GlobalSettings.mainWindow.hide()
940
  except Exception as e:
941
+ show_error("Error in changeto_population_Analysis() in main.", e)
 
 
 
 
 
 
 
 
 
 
 
942
 
943
  def annotation_information(self):
944
  try:
 
954
  msgBox.exec()
955
 
956
  except Exception as e:
957
+ show_error("Error in annotation_information() in main.", e)
958
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
959
  @QtCore.pyqtSlot()
960
  def view_results(self):
961
  try:
 
967
  self.Results.show()
968
  self.hide()
969
  except Exception as e:
970
+ show_error("Error in view_results() in main", e)
 
 
 
 
 
 
 
 
 
971
 
972
+
973
 
974
  def closeFunction(self):
975
  try:
 
979
  except AttributeError:
980
  print("No NCBI window to close.")
981
 
 
982
  self.myClosingWindow.get_files()
983
+ center_ui(self.myClosingWindow)
984
  self.myClosingWindow.show()
985
  except Exception as e:
986
+ show_error("Error in closeFunction() in main", e)
987
 
988
  def close_app(self):
989
  try:
 
997
  self.closeFunction()
998
  self.close()
999
  except Exception as e:
1000
+ show_error("Error in close_app() in main", e)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
GenBankParse.py → views/GenBankParse.py RENAMED
File without changes
views/NewEndonuclease.py ADDED
@@ -0,0 +1,228 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sys, os
2
+ from PyQt5 import QtWidgets, uic, QtGui, QtCore, Qt
3
+ import models.GlobalSettings as GlobalSettings
4
+ from PyQt5.QtGui import QIntValidator
5
+ import traceback
6
+ import math
7
+ from utils.ui import show_message, show_error, scale_ui, center_ui
8
+
9
+ logger = GlobalSettings.logger
10
+
11
+ class NewEndonuclease(QtWidgets.QMainWindow):
12
+ def __init__(self):
13
+ print("Initializing NewEndonuclease class")
14
+ try:
15
+ super(NewEndonuclease, self).__init__()
16
+ uic.loadUi(GlobalSettings.appdir + 'ui/newendonuclease.ui', self)
17
+ self.setWindowIcon(Qt.QIcon(GlobalSettings.appdir + "cas9image.ico"))
18
+ self.setWindowTitle('New Endonuclease')
19
+ self.error = False
20
+ pamFlag = False
21
+
22
+ self.onList = []
23
+ self.offList = []
24
+
25
+ self.onList, self.offList = self.get_on_off_data() ### Call function to fill on- and off- data name lists
26
+
27
+ for name in self.onList: ### Add on-target names to drop-down
28
+ self.comboBox.addItem(str(name))
29
+
30
+ for name in self.offList: ### Add off-target names to drop-down
31
+ self.comboBox_2.addItem(str(name))
32
+
33
+ self.submit_button.clicked.connect(self.submit)
34
+ self.cancel_button.clicked.connect(self.cancel)
35
+
36
+ ### Set up validators for input fields:
37
+ reg_ex1 = QtCore.QRegExp("[^/\\\\_]+") # No slashes or underscores
38
+ reg_ex2 = QtCore.QRegExp("[^/\\\\_\\s]+") # No slashes, underscores, or spaces
39
+ reg_ex3 = QtCore.QRegExp("[acdefghiklmnpqrstvwyACDEFGHIKLMNPQRSTVWY\S]+") # Only approved PAM characters and no spaces
40
+ input_validator1 = QtGui.QRegExpValidator(reg_ex1, self)
41
+ input_validator2 = QtGui.QRegExpValidator(reg_ex2, self)
42
+ input_validator3 = QtGui.QRegExpValidator(reg_ex3, self)
43
+ self.organism_name.setValidator(input_validator1)
44
+ self.abbreviation.setValidator(input_validator2)
45
+ self.pam_sequence.setValidator(input_validator3)
46
+
47
+ self.seed_length.setValidator(QIntValidator(0,30,self.seed_length))
48
+ self.five_length.setValidator(QIntValidator(0,20,self.five_length))
49
+ self.three_length.setValidator(QIntValidator(0,20,self.three_length))
50
+
51
+ groupbox_style = """
52
+ QGroupBox:title{subcontrol-origin: margin;
53
+ left: 10px;
54
+ padding: 0 5px 0 5px;}
55
+ QGroupBox#groupBox{border: 2px solid rgb(111,181,110);
56
+ border-radius: 9px;
57
+ font: bold 14pt 'Arial';
58
+ margin-top: 10px;}"""
59
+
60
+ self.groupBox.setStyleSheet(groupbox_style)
61
+ self.groupBox_2.setStyleSheet(groupbox_style.replace("groupBox","groupBox_2"))
62
+ self.groupBox_3.setStyleSheet(groupbox_style.replace("groupBox","groupBox_3"))
63
+
64
+ scale_ui(self, custom_scale_width=480, custom_scale_height=615)
65
+ except Exception as e:
66
+ show_error("Error initializing NewEndonuclease class.", e)
67
+
68
+ #helper function for writing new endo information to CASPERinfo - used by submit()
69
+ def writeNewEndonuclease(self, newEndonucleaseStr):
70
+ try:
71
+ with open(GlobalSettings.appdir + 'CASPERinfo', 'r') as f, open(GlobalSettings.appdir + "new_file", 'w+') as f1:
72
+ for line in f:
73
+ f1.write(line)
74
+ if 'ENDONUCLEASES' in line:
75
+ f1.write(newEndonucleaseStr + '\n') # Move f1.write(line) above, to write above instead
76
+ os.remove(GlobalSettings.appdir + "CASPERinfo")
77
+ os.rename(GlobalSettings.appdir + "new_file",
78
+ GlobalSettings.appdir + "CASPERinfo") # Rename the new file
79
+ except Exception as e:
80
+ show_error("Error in writeNewEndonuclease() in New Endonuclease.", e)
81
+
82
+ #submit new endo to CASPERinfo file
83
+ def submit(self):
84
+ try:
85
+ # This is executed when the button is pressed
86
+ name = str(self.organism_name.text())
87
+ abbr = str(self.abbreviation.text())
88
+ crisprtype = str(self.crispr_type.text())
89
+ seed_len = str(self.seed_length.text())
90
+ five_len = str(self.five_length.text())
91
+ three_len = str(self.three_length.text())
92
+ pam = str(self.pam_sequence.text()).upper()
93
+ ### Check for multiple PAMs and format if present
94
+ if len(pam.split(','))>0:
95
+ pam = [x.strip() for x in pam.split(',')]
96
+ pam = ",".join(pam)
97
+ ### Check for PAM directionality
98
+ if self.five_pam.isChecked():
99
+ pam_dir = str(5)
100
+ else:
101
+ pam_dir = str(3)
102
+ on_scoring = str(self.comboBox.currentText())
103
+ off_scoring = str(self.comboBox_2.currentText())
104
+ length = len(seed_len) + len(five_len) + len(three_len)
105
+ argument_list = [abbr, pam, five_len, seed_len, three_len, pam_dir, name, crisprtype, on_scoring, off_scoring]
106
+ validPAM = ('A', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'V', 'W', 'Y')
107
+ self.error = False;
108
+
109
+ ### Error checking for PAM alphabet
110
+ for letter in pam:
111
+ if (letter not in validPAM):
112
+ show_message(
113
+ fontSize=12,
114
+ icon=QtWidgets.QMessageBox.Icon.Critical,
115
+ title="Invalid PAM",
116
+ message="Invalid characters in PAM Sequence."
117
+ )
118
+ return True
119
+ ### Error checking for filling out all fields
120
+ for arg in argument_list:
121
+ if ';' in arg:
122
+ show_message(
123
+ fontSize=12,
124
+ icon=QtWidgets.QMessageBox.Icon.Critical,
125
+ title="Invalid Semicolon",
126
+ message="Invalid character used: ';'."
127
+ )
128
+ return True
129
+ elif arg == "":
130
+ show_message(
131
+ fontSize=12,
132
+ icon=QtWidgets.QMessageBox.Icon.Critical,
133
+ title="Empty Field",
134
+ message="Please fill in all fields."
135
+ )
136
+ return True
137
+ else:
138
+ pass
139
+
140
+ ### Check for duplicate endo abbreviations
141
+ for key in GlobalSettings.mainWindow.organisms_to_endos:
142
+ endo = GlobalSettings.mainWindow.organisms_to_endos[key]
143
+ if abbr in endo:
144
+ show_message(
145
+ fontSize=12,
146
+ icon=QtWidgets.QMessageBox.Icon.Critical,
147
+ title="Duplicate endo name.",
148
+ message="The given abbreviation already exists. Please choose a unique identifier."
149
+ )
150
+ return True
151
+ else:
152
+ pass
153
+
154
+ myString = ""
155
+ for i, arg in enumerate(argument_list):
156
+ if i == len(argument_list)-1: ### Last argument in list
157
+ myString += str(arg)
158
+ else:
159
+ myString += str(arg) + ";"
160
+
161
+ self.writeNewEndonuclease(myString)
162
+
163
+ ### Refresh endonuclease dropdown in New Genome
164
+ GlobalSettings.mainWindow.newGenome.fillEndo()
165
+
166
+ self.clear_all()
167
+ self.close()
168
+ except Exception as e:
169
+ show_error("Error in submit() in New Endonuclease.", e)
170
+
171
+ #cancel and close window
172
+ def cancel(self):
173
+ try:
174
+ self.clear_all()
175
+ self.close()
176
+ except Exception as e:
177
+ show_error("Error in cancel() in New Endonuclease.", e)
178
+
179
+ # This function clears all of the line edits
180
+ def clear_all(self):
181
+ try:
182
+ self.organism_name.clear()
183
+ self.abbreviation.clear()
184
+ self.crispr_type.clear()
185
+ self.seed_length.clear()
186
+ self.five_length.clear()
187
+ self.three_length.clear()
188
+ self.pam_sequence.clear()
189
+ except Exception as e:
190
+ show_error("Error in clear_all() in New Endonuclease.", e)
191
+
192
+ # This function parses CASPERinfo to return the names (in lists) of all on-target and off-target scoring data
193
+ def get_on_off_data(self):
194
+ try:
195
+ filename = GlobalSettings.appdir + "CASPERinfo"
196
+ retList_on = []
197
+ retList_off = []
198
+ with open(filename, 'r') as f:
199
+ lines = f.readlines()
200
+ for i, line in enumerate(lines):
201
+ line = str(line)
202
+ if "ON-TARGET DATA" in line:
203
+ index = i
204
+ while "-----" not in line:
205
+ if "DATA:" in line:
206
+ retList_on.append(line.split("DATA:")[-1].strip()) ### Append name of scoring data to on-target name list
207
+ line = lines[index+1]
208
+ index += 1
209
+ else:
210
+ line = lines[index+1]
211
+ index += 1
212
+ continue
213
+ elif "OFF-TARGET MATRICES" in line:
214
+ index = i
215
+ while "-----" not in line:
216
+ if "MATRIX:" in line:
217
+ retList_off.append(line.split("MATRIX:")[-1].strip()) ### Append name of scoring data to off-target name list
218
+ line = lines[index+1]
219
+ index += 1
220
+ else:
221
+ line = lines[index+1]
222
+ index += 1
223
+ continue
224
+ else:
225
+ continue
226
+ return retList_on, retList_off
227
+ except Exception as e:
228
+ show_error("Error in get_on_off_data() in New Endonuclease.", e)
NewGenome.py → views/NewGenome.py RENAMED
@@ -1,17 +1,16 @@
1
  from ast import Global
2
  import os
3
  from PyQt5 import QtWidgets, uic, QtGui, QtCore, Qt
4
- import GlobalSettings
5
  from functools import partial
6
- from Algorithms import SeqTranslate
7
  import webbrowser
8
  import platform
9
  import traceback
10
  import math
 
 
11
 
12
- from ui_utils import center_ui
13
-
14
- #global logger
15
  logger = GlobalSettings.logger
16
 
17
  def iter_except(function, exception):
@@ -22,13 +21,12 @@ def iter_except(function, exception):
22
  except exception:
23
  return
24
 
25
-
26
  #UI prompt for when the user has finished running jobs in new genome to allow them to choose where the want to proceed
27
  class goToPrompt(QtWidgets.QMainWindow):
28
  def __init__(self):
29
  try:
30
  super(goToPrompt, self).__init__()
31
- uic.loadUi(GlobalSettings.appdir + 'newgenomenavigationpage.ui', self)
32
 
33
  groupbox_style = """
34
  QGroupBox:title{subcontrol-origin: margin;
@@ -39,125 +37,20 @@ class goToPrompt(QtWidgets.QMainWindow):
39
  font: bold 14pt 'Arial';
40
  margin-top: 10px;}"""
41
  self.groupBox.setStyleSheet(groupbox_style)
42
- self.scaleUI()
43
  self.setWindowTitle("New Genome")
44
  self.setWindowIcon(Qt.QIcon(GlobalSettings.appdir + "cas9image.ico"))
45
  self.hide()
46
 
47
  except Exception as e:
48
- logger.critical("Unable to initialize goToPrompt class in New Genome.")
49
- logger.critical(e)
50
- logger.critical(traceback.format_exc())
51
- msgBox = QtWidgets.QMessageBox()
52
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
53
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
54
- msgBox.setWindowTitle("Fatal Error")
55
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
56
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
57
- msgBox.exec()
58
-
59
- exit(-1)
60
-
61
- #scale UI based on current screen
62
- def scaleUI(self):
63
- try:
64
- self.repaint()
65
- QtWidgets.QApplication.processEvents()
66
-
67
- screen = self.screen()
68
- dpi = screen.physicalDotsPerInch()
69
- width = screen.geometry().width()
70
- height = screen.geometry().height()
71
-
72
- # font scaling
73
- fontSize = 12
74
- self.fontSize = fontSize
75
- self.centralWidget().setStyleSheet("font: " + str(fontSize) + "pt 'Arial';")
76
-
77
- self.adjustSize()
78
-
79
- currentWidth = self.size().width()
80
- currentHeight = self.size().height()
81
-
82
- # window scaling
83
- # 1920x1080 => 550x200
84
- scaledWidth = int((width * 575) / 1920)
85
- scaledHeight = int((height * 175) / 1080)
86
-
87
- if scaledHeight < currentHeight:
88
- scaledHeight = currentHeight
89
- if scaledWidth < currentWidth:
90
- scaledWidth = currentWidth
91
-
92
- screen = QtWidgets.QApplication.desktop().screenNumber(QtWidgets.QApplication.desktop().cursor().pos())
93
- centerPoint = QtWidgets.QApplication.desktop().screenGeometry(screen).center()
94
- x = centerPoint.x()
95
- y = centerPoint.y()
96
- x = x - (math.ceil(scaledWidth / 2))
97
- y = y - (math.ceil(scaledHeight / 2))
98
- self.setGeometry(x, y, scaledWidth, scaledHeight)
99
-
100
- self.repaint()
101
- QtWidgets.QApplication.processEvents()
102
-
103
- except Exception as e:
104
- logger.critical("Error in scaleUI() in new genome navigation window.")
105
- logger.critical(e)
106
- logger.critical(traceback.format_exc())
107
- msgBox = QtWidgets.QMessageBox()
108
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
109
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
110
- msgBox.setWindowTitle("Fatal Error")
111
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
112
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
113
- msgBox.exec()
114
-
115
-
116
- exit(-1)
117
-
118
- #center UI on current screen
119
- def centerUI(self):
120
- try:
121
- self.repaint()
122
- QtWidgets.QApplication.processEvents()
123
-
124
- #center window on current screen
125
- width = self.width()
126
- height = self.height()
127
- screen = QtWidgets.QApplication.desktop().screenNumber(QtWidgets.QApplication.desktop().cursor().pos())
128
- centerPoint = QtWidgets.QApplication.desktop().screenGeometry(screen).center()
129
- x = centerPoint.x()
130
- y = centerPoint.y()
131
- x = x - (math.ceil(width / 2))
132
- y = y - (math.ceil(height / 2))
133
- self.setGeometry(x, y, width, height)
134
-
135
- self.repaint()
136
- QtWidgets.QApplication.processEvents()
137
- except Exception as e:
138
- logger.critical("Error in centerUI() in new genome navigation window.")
139
- logger.critical(e)
140
- logger.critical(traceback.format_exc())
141
- msgBox = QtWidgets.QMessageBox()
142
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
143
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
144
- msgBox.setWindowTitle("Fatal Error")
145
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
146
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
147
- msgBox.exec()
148
-
149
-
150
- exit(-1)
151
-
152
 
153
  #New genome class to allow users to generate new CSPR files
154
  class NewGenome(QtWidgets.QMainWindow):
155
-
156
- #init class
157
  def __init__(self, info_path):
158
  try:
159
  super(NewGenome, self).__init__()
160
- uic.loadUi(GlobalSettings.appdir + 'NewGenome.ui', self)
161
  self.setWindowTitle('New Genome')
162
  self.setWindowTitle('New Genome')
163
  self.info_path = info_path
@@ -203,7 +96,6 @@ class NewGenome(QtWidgets.QMainWindow):
203
  self.seqTrans = SeqTranslate()
204
  self.exit = False
205
 
206
-
207
  self.first = False
208
  #show functionalities on window
209
  self.fillEndo()
@@ -219,7 +111,6 @@ class NewGenome(QtWidgets.QMainWindow):
219
  self.job_Table.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents)
220
  self.fin_index=0
221
 
222
-
223
  self.mwfg = self.frameGeometry() ##Center window
224
  self.cp = QtWidgets.QDesktopWidget().availableGeometry().center() ##Center window
225
  self.total_chrom_count = 0
@@ -227,8 +118,8 @@ class NewGenome(QtWidgets.QMainWindow):
227
  self.progress = 0
228
 
229
  #toolbar button actions
230
- self.visit_repo.triggered.connect(self.visit_repo_func)
231
- self.go_ncbi.triggered.connect(self.open_ncbi_web_page)
232
 
233
  self.comboBoxEndo.currentIndexChanged.connect(self.changeEndos)
234
 
@@ -260,24 +151,10 @@ class NewGenome(QtWidgets.QMainWindow):
260
  self.strainName.setValidator(input_validator1)
261
  self.orgCode.setValidator(input_validator2)
262
 
263
- ### Scale UI ?
264
- # scale_ui(self)
265
  self.first_show = True
266
-
267
  except Exception as e:
268
- logger.critical("Unable to initialize New Genome class.")
269
- logger.critical(e)
270
- logger.critical(traceback.format_exc())
271
- msgBox = QtWidgets.QMessageBox()
272
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
273
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
274
- msgBox.setWindowTitle("Fatal Error")
275
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
276
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
277
- msgBox.exec()
278
-
279
-
280
- exit(-1)
281
 
282
  def launch_newEndonuclease(self):
283
  try:
@@ -286,17 +163,7 @@ class NewGenome(QtWidgets.QMainWindow):
286
  GlobalSettings.mainWindow.newEndonuclease.show()
287
  GlobalSettings.mainWindow.newEndonuclease.activateWindow()
288
  except Exception as e:
289
- logger.critical("Error in launch_newEndonuclease() in New Genome.")
290
- logger.critical(e)
291
- logger.critical(traceback.format_exc())
292
- msgBox = QtWidgets.QMessageBox()
293
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
294
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
295
- msgBox.setWindowTitle("Fatal Error")
296
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
297
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
298
- msgBox.exec()
299
- exit(-1)
300
 
301
  #open the ncbi search tool window
302
  def open_ncbi_tool(self):
@@ -312,21 +179,8 @@ class NewGenome(QtWidgets.QMainWindow):
312
  GlobalSettings.mainWindow.ncbi.show()
313
  GlobalSettings.mainWindow.ncbi.activateWindow()
314
  except Exception as e:
315
- logger.critical("Error in open_ncbi_tool() in New Genome.")
316
- logger.critical(e)
317
- logger.critical(traceback.format_exc())
318
- msgBox = QtWidgets.QMessageBox()
319
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
320
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
321
- msgBox.setWindowTitle("Fatal Error")
322
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
323
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
324
- msgBox.exec()
325
-
326
-
327
- exit(-1)
328
 
329
- #remove jobs from queue
330
  def remove_from_queue(self):
331
  try:
332
  while(True):
@@ -335,19 +189,7 @@ class NewGenome(QtWidgets.QMainWindow):
335
  break
336
  self.job_Table.removeRow(indexes[0].row())
337
  except Exception as e:
338
- logger.critical("Error in remove_from_queue() in New Genome.")
339
- logger.critical(e)
340
- logger.critical(traceback.format_exc())
341
- msgBox = QtWidgets.QMessageBox()
342
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
343
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
344
- msgBox.setWindowTitle("Fatal Error")
345
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
346
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
347
- msgBox.exec()
348
-
349
-
350
- exit(-1)
351
 
352
  #prompt user with file browser to select fasta/fna files
353
  def selectFasta(self):
@@ -356,33 +198,18 @@ class NewGenome(QtWidgets.QMainWindow):
356
  myFile = QtWidgets.QFileDialog.getOpenFileName(filed, "Choose a File")
357
  if (myFile[0] != ""):
358
  if not myFile[0].endswith(".fa") and not myFile[0].endswith(".fna") and not myFile[0].endswith(".fasta"):
359
- msgBox = QtWidgets.QMessageBox()
360
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
361
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
362
- msgBox.setWindowTitle("File Selection Error")
363
- msgBox.setText("You have selected an incorrect type of file. Please choose a FASTA/FNA file.")
364
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
365
- msgBox.exec()
366
-
367
  return
368
  else:
369
  self.file = myFile[0]
370
  self.selectedFile.setText(str(myFile[0]))
371
-
372
  except Exception as e:
373
- logger.critical("Error in selectFasta() in New Genome.")
374
- logger.critical(e)
375
- logger.critical(traceback.format_exc())
376
- msgBox = QtWidgets.QMessageBox()
377
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
378
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
379
- msgBox.setWindowTitle("Fatal Error")
380
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
381
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
382
- msgBox.exec()
383
-
384
-
385
- exit(-1)
386
 
387
  #submit jobs to queue
388
  def submit(self):
@@ -393,15 +220,13 @@ class NewGenome(QtWidgets.QMainWindow):
393
  if len(self.file) == 0:
394
  warning = warning + "You need to select a file."
395
  if len(warning) != 0:
396
- msgBox = QtWidgets.QMessageBox()
397
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
398
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
399
- msgBox.setWindowTitle("Required Information")
400
- msgBox.setText(warning)
401
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
402
- msgBox.exec()
403
  return
404
-
405
  if len(self.strainName.text()) == 0:
406
  warning = warning + "\nIt is recommended to include the organism's subspecies/strain."
407
  if len(self.orgCode.text()) == 0:
@@ -419,8 +244,6 @@ class NewGenome(QtWidgets.QMainWindow):
419
  if msgBox.result() == QtWidgets.QMessageBox.No:
420
  return
421
 
422
-
423
-
424
  #endo, pam, repeats, directionality, five length, seed length, three length, orgcode, output path, CASPERinfo path, fna path, orgName, notes, on target matrix
425
  args = self.Endos[self.comboBoxEndo.currentText()][0]
426
  args += " " + self.Endos[self.comboBoxEndo.currentText()][1]
@@ -458,14 +281,12 @@ class NewGenome(QtWidgets.QMainWindow):
458
 
459
  tmp = self.orgName.text()+ " " + self.strainName.text() + " " + self.Endos[self.comboBoxEndo.currentText()][0] + " " + self.orgCode.text()
460
  if tmp in self.check_strings:
461
- msgBox = QtWidgets.QMessageBox()
462
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
463
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
464
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Question)
465
- msgBox.setWindowTitle("Duplicate Entry")
466
- msgBox.setText("You have submitted a duplicate entry. Consider changing the organism code or strain name to differentiate closely related strains.")
467
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
468
- msgBox.exec()
469
  return
470
  name = self.orgCode.text() + "_" + str(self.Endos[self.comboBoxEndo.currentText()][0])
471
  rowPosition = self.job_Table.rowCount()
@@ -476,19 +297,7 @@ class NewGenome(QtWidgets.QMainWindow):
476
  self.check_strings.append(tmp)
477
  self.JobsQueue.append(args)
478
  except Exception as e:
479
- logger.critical("Error in submit() in New Genome.")
480
- logger.critical(e)
481
- logger.critical(traceback.format_exc())
482
- msgBox = QtWidgets.QMessageBox()
483
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
484
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
485
- msgBox.setWindowTitle("Fatal Error")
486
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
487
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
488
- msgBox.exec()
489
-
490
-
491
- exit(-1)
492
 
493
  #fill the endo dropdown
494
  def fillEndo(self):
@@ -535,19 +344,7 @@ class NewGenome(QtWidgets.QMainWindow):
535
  #reconnect signal
536
  self.comboBoxEndo.currentIndexChanged.connect(self.changeEndos)
537
  except Exception as e:
538
- logger.critical("Error in fillEndo() in New Genome.")
539
- logger.critical(e)
540
- logger.critical(traceback.format_exc())
541
- msgBox = QtWidgets.QMessageBox()
542
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
543
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
544
- msgBox.setWindowTitle("Fatal Error")
545
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
546
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
547
- msgBox.exec()
548
-
549
-
550
- exit(-1)
551
 
552
  #event handler for endo changing - update endo length data
553
  def changeEndos(self):
@@ -557,19 +354,7 @@ class NewGenome(QtWidgets.QMainWindow):
557
  self.five_length.setText(self.Endos[key][2])
558
  self.three_length.setText(self.Endos[key][4])
559
  except Exception as e:
560
- logger.critical("Error in changeEndos() in New Genome.")
561
- logger.critical(e)
562
- logger.critical(traceback.format_exc())
563
- msgBox = QtWidgets.QMessageBox()
564
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
565
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
566
- msgBox.setWindowTitle("Fatal Error")
567
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
568
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
569
- msgBox.exec()
570
-
571
-
572
- exit(-1)
573
 
574
  #check if endo is 3' or 5'
575
  def endo_settings(self):
@@ -580,19 +365,7 @@ class NewGenome(QtWidgets.QMainWindow):
580
  elif int(self.seqTrans.endo_info[self.Endos[self.comboBoxEndo.currentText()][0]][3]) == 5:
581
  self.pamBox.setChecked(1)
582
  except Exception as e:
583
- logger.critical("Error in endo_settings() in New Genome.")
584
- logger.critical(e)
585
- logger.critical(traceback.format_exc())
586
- msgBox = QtWidgets.QMessageBox()
587
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
588
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
589
- msgBox.setWindowTitle("Fatal Error")
590
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
591
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
592
- msgBox.exec()
593
-
594
-
595
- exit(-1)
596
 
597
  #wrapper for running jobs
598
  def run_jobs_wrapper(self):
@@ -605,19 +378,7 @@ class NewGenome(QtWidgets.QMainWindow):
605
  self.indexes.append(index.row())
606
  self.run_job()
607
  except Exception as e:
608
- logger.critical("Error in run_jobs_wrapper() in New Genome.")
609
- logger.critical(e)
610
- logger.critical(traceback.format_exc())
611
- msgBox = QtWidgets.QMessageBox()
612
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
613
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
614
- msgBox.setWindowTitle("Fatal Error")
615
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
616
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
617
- msgBox.exec()
618
-
619
-
620
- exit(-1)
621
 
622
  #run job in queue
623
  def run_job(self):
@@ -680,28 +441,14 @@ class NewGenome(QtWidgets.QMainWindow):
680
  self.process.readyReadStandardOutput.connect(partial(output_stdout, self.process))
681
  self.process.start(program)
682
  else:
683
- msgBox = QtWidgets.QMessageBox()
684
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
685
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
686
- msgBox.setWindowTitle("No Jobs To Run")
687
- msgBox.setText("No jobs are in the queue to run. Please add a job before running.")
688
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
689
- msgBox.exec()
690
-
691
  except Exception as e:
692
- logger.critical("Error in run_job() in New Genome.")
693
- logger.critical(e)
694
- logger.critical(traceback.format_exc())
695
- msgBox = QtWidgets.QMessageBox()
696
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
697
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
698
- msgBox.setWindowTitle("Fatal Error")
699
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
700
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
701
- msgBox.exec()
702
-
703
-
704
- exit(-1)
705
 
706
  #even handler for when jobs finish execution
707
  def upon_process_finishing(self):
@@ -717,23 +464,11 @@ class NewGenome(QtWidgets.QMainWindow):
717
  self.run_job()
718
  else:
719
  #prompt user if they want to analyze their new files
720
- self.goToPrompt.centerUI()
721
  self.goToPrompt.show()
722
  self.goToPrompt.activateWindow()
723
  except Exception as e:
724
- logger.critical("Error in upon_process_finishing() in New Genome.")
725
- logger.critical(e)
726
- logger.critical(traceback.format_exc())
727
- msgBox = QtWidgets.QMessageBox()
728
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
729
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
730
- msgBox.setWindowTitle("Fatal Error")
731
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
732
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
733
- msgBox.exec()
734
-
735
-
736
- exit(-1)
737
 
738
  #clear the job table
739
  def clear_all(self):
@@ -754,19 +489,7 @@ class NewGenome(QtWidgets.QMainWindow):
754
  self.progressBar.setValue(0)
755
  self.first = False
756
  except Exception as e:
757
- logger.critical("Error in clear_all() in New Genome.")
758
- logger.critical(e)
759
- logger.critical(traceback.format_exc())
760
- msgBox = QtWidgets.QMessageBox()
761
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
762
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
763
- msgBox.setWindowTitle("Fatal Error")
764
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
765
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
766
- msgBox.exec()
767
-
768
-
769
- exit(-1)
770
 
771
  #reset the whole form
772
  def reset(self):
@@ -780,58 +503,8 @@ class NewGenome(QtWidgets.QMainWindow):
780
  self.output_browser.setText("Waiting for program initiation...")
781
  self.file = ""
782
  except Exception as e:
783
- logger.critical("Error in reset() in New Genome.")
784
- logger.critical(e)
785
- logger.critical(traceback.format_exc())
786
- msgBox = QtWidgets.QMessageBox()
787
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
788
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
789
- msgBox.setWindowTitle("Fatal Error")
790
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
791
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
792
- msgBox.exec()
793
-
794
-
795
- exit(-1)
796
-
797
- #menu button for launching NCBI web page
798
- def open_ncbi_web_page(self):
799
- try:
800
- webbrowser.open('https://www.ncbi.nlm.nih.gov/', new=2)
801
- except Exception as e:
802
- logger.critical("Error in open_ncbi_web_page() in New Genome.")
803
- logger.critical(e)
804
- logger.critical(traceback.format_exc())
805
- msgBox = QtWidgets.QMessageBox()
806
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
807
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
808
- msgBox.setWindowTitle("Fatal Error")
809
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
810
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
811
- msgBox.exec()
812
-
813
-
814
- exit(-1)
815
-
816
- #menu button for launching github repo
817
- def visit_repo_func(self):
818
- try:
819
- webbrowser.open('https://github.com/TrinhLab/CASPERapp')
820
- except Exception as e:
821
- logger.critical("Error in visit_repo_func() in New Genome.")
822
- logger.critical(e)
823
- logger.critical(traceback.format_exc())
824
- msgBox = QtWidgets.QMessageBox()
825
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
826
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
827
- msgBox.setWindowTitle("Fatal Error")
828
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
829
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
830
- msgBox.exec()
831
-
832
-
833
- exit(-1)
834
-
835
  #event handler for user wanting to close the window
836
  def closeEvent(self, event):
837
  try:
@@ -861,7 +534,6 @@ class NewGenome(QtWidgets.QMainWindow):
861
  else:
862
  self.exit = False
863
  event.accept()
864
-
865
  else:
866
  self.process.kill()
867
  self.clear_all()
@@ -881,19 +553,7 @@ class NewGenome(QtWidgets.QMainWindow):
881
  GlobalSettings.mainWindow.show()
882
  event.accept()
883
  except Exception as e:
884
- logger.critical("Error in closeEvent() in New Genome.")
885
- logger.critical(e)
886
- logger.critical(traceback.format_exc())
887
- msgBox = QtWidgets.QMessageBox()
888
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
889
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
890
- msgBox.setWindowTitle("Fatal Error")
891
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
892
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
893
- msgBox.exec()
894
-
895
-
896
- exit(-1)
897
 
898
  #event handler for user wanting to go to Main once jobs complete
899
  def continue_to_main(self):
@@ -918,11 +578,9 @@ class NewGenome(QtWidgets.QMainWindow):
918
  msgBox.addButton(QtWidgets.QMessageBox.StandardButton.No)
919
  msgBox.exec()
920
 
921
-
922
  if (msgBox.result() == QtWidgets.QMessageBox.Yes):
923
  self.exit = True
924
  self.close()
925
-
926
  else:
927
  self.process.kill()
928
  self.clear_all()
@@ -943,19 +601,7 @@ class NewGenome(QtWidgets.QMainWindow):
943
  GlobalSettings.mainWindow.show()
944
  self.hide()
945
  except Exception as e:
946
- logger.critical("Error in continue_to_main() in New Genome.")
947
- logger.critical(e)
948
- logger.critical(traceback.format_exc())
949
- msgBox = QtWidgets.QMessageBox()
950
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
951
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
952
- msgBox.setWindowTitle("Fatal Error")
953
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
954
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
955
- msgBox.exec()
956
-
957
-
958
- exit(-1)
959
 
960
  #event handler for user wanting to go to multi-targeting once jobs complete
961
  def continue_to_MT(self):
@@ -1007,19 +653,7 @@ class NewGenome(QtWidgets.QMainWindow):
1007
  GlobalSettings.MTWin.show()
1008
  self.hide()
1009
  except Exception as e:
1010
- logger.critical("Error in continue_to_MT() in New Genome.")
1011
- logger.critical(e)
1012
- logger.critical(traceback.format_exc())
1013
- msgBox = QtWidgets.QMessageBox()
1014
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1015
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1016
- msgBox.setWindowTitle("Fatal Error")
1017
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1018
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1019
- msgBox.exec()
1020
-
1021
-
1022
- exit(-1)
1023
 
1024
  #event handler for user wanting to go to population analysis once jobs complete
1025
  def continue_to_pop(self):
@@ -1068,16 +702,4 @@ class NewGenome(QtWidgets.QMainWindow):
1068
  GlobalSettings.pop_Analysis.show()
1069
  self.hide()
1070
  except Exception as e:
1071
- logger.critical("Error in continue_to_pop() in New Genome.")
1072
- logger.critical(e)
1073
- logger.critical(traceback.format_exc())
1074
- msgBox = QtWidgets.QMessageBox()
1075
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
1076
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
1077
- msgBox.setWindowTitle("Fatal Error")
1078
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
1079
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
1080
- msgBox.exec()
1081
-
1082
-
1083
- exit(-1)
 
1
  from ast import Global
2
  import os
3
  from PyQt5 import QtWidgets, uic, QtGui, QtCore, Qt
4
+ import models.GlobalSettings as GlobalSettings
5
  from functools import partial
6
+ from utils.Algorithms import SeqTranslate
7
  import webbrowser
8
  import platform
9
  import traceback
10
  import math
11
+ from utils.ui import show_message, show_error, scale_ui, center_ui
12
+ from utils.web import ncbi_page, repo_page
13
 
 
 
 
14
  logger = GlobalSettings.logger
15
 
16
  def iter_except(function, exception):
 
21
  except exception:
22
  return
23
 
 
24
  #UI prompt for when the user has finished running jobs in new genome to allow them to choose where the want to proceed
25
  class goToPrompt(QtWidgets.QMainWindow):
26
  def __init__(self):
27
  try:
28
  super(goToPrompt, self).__init__()
29
+ uic.loadUi(GlobalSettings.appdir + 'ui/newgenomenavigationpage.ui', self)
30
 
31
  groupbox_style = """
32
  QGroupBox:title{subcontrol-origin: margin;
 
37
  font: bold 14pt 'Arial';
38
  margin-top: 10px;}"""
39
  self.groupBox.setStyleSheet(groupbox_style)
40
+ scale_ui(self, custom_scale_width=575, custom_scale_height=175)
41
  self.setWindowTitle("New Genome")
42
  self.setWindowIcon(Qt.QIcon(GlobalSettings.appdir + "cas9image.ico"))
43
  self.hide()
44
 
45
  except Exception as e:
46
+ show_error("Unable to initialize goToPrompt class in New Genome.", e)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
 
48
  #New genome class to allow users to generate new CSPR files
49
  class NewGenome(QtWidgets.QMainWindow):
 
 
50
  def __init__(self, info_path):
51
  try:
52
  super(NewGenome, self).__init__()
53
+ uic.loadUi(GlobalSettings.appdir + 'ui/NewGenome.ui', self)
54
  self.setWindowTitle('New Genome')
55
  self.setWindowTitle('New Genome')
56
  self.info_path = info_path
 
96
  self.seqTrans = SeqTranslate()
97
  self.exit = False
98
 
 
99
  self.first = False
100
  #show functionalities on window
101
  self.fillEndo()
 
111
  self.job_Table.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents)
112
  self.fin_index=0
113
 
 
114
  self.mwfg = self.frameGeometry() ##Center window
115
  self.cp = QtWidgets.QDesktopWidget().availableGeometry().center() ##Center window
116
  self.total_chrom_count = 0
 
118
  self.progress = 0
119
 
120
  #toolbar button actions
121
+ self.visit_repo.triggered.connect(repo_page)
122
+ self.go_ncbi.triggered.connect(ncbi_page)
123
 
124
  self.comboBoxEndo.currentIndexChanged.connect(self.changeEndos)
125
 
 
151
  self.strainName.setValidator(input_validator1)
152
  self.orgCode.setValidator(input_validator2)
153
 
154
+ scale_ui(self, custom_scale_width=850, custom_scale_height=750)
 
155
  self.first_show = True
 
156
  except Exception as e:
157
+ show_error("Error initializing New Genome class.", e)
 
 
 
 
 
 
 
 
 
 
 
 
158
 
159
  def launch_newEndonuclease(self):
160
  try:
 
163
  GlobalSettings.mainWindow.newEndonuclease.show()
164
  GlobalSettings.mainWindow.newEndonuclease.activateWindow()
165
  except Exception as e:
166
+ show_error("Error in launch_newEndonuclease() in New Genome.", e)
 
 
 
 
 
 
 
 
 
 
167
 
168
  #open the ncbi search tool window
169
  def open_ncbi_tool(self):
 
179
  GlobalSettings.mainWindow.ncbi.show()
180
  GlobalSettings.mainWindow.ncbi.activateWindow()
181
  except Exception as e:
182
+ show_error("Error in open_ncbi_tool() in New Genome.", e)
 
 
 
 
 
 
 
 
 
 
 
 
183
 
 
184
  def remove_from_queue(self):
185
  try:
186
  while(True):
 
189
  break
190
  self.job_Table.removeRow(indexes[0].row())
191
  except Exception as e:
192
+ show_error("Error in remove_from_queue() in New Genome.", e)
 
 
 
 
 
 
 
 
 
 
 
 
193
 
194
  #prompt user with file browser to select fasta/fna files
195
  def selectFasta(self):
 
198
  myFile = QtWidgets.QFileDialog.getOpenFileName(filed, "Choose a File")
199
  if (myFile[0] != ""):
200
  if not myFile[0].endswith(".fa") and not myFile[0].endswith(".fna") and not myFile[0].endswith(".fasta"):
201
+ show_message(
202
+ fontSize=12,
203
+ icon=QtWidgets.QMessageBox.Icon.Critical,
204
+ title="File Selection Error",
205
+ message="You have selected an incorrect type of file. Please choose a FASTA/FNA file."
206
+ )
 
 
207
  return
208
  else:
209
  self.file = myFile[0]
210
  self.selectedFile.setText(str(myFile[0]))
 
211
  except Exception as e:
212
+ show_error("Error in selectFasta() in New Genome.", e)
 
 
 
 
 
 
 
 
 
 
 
 
213
 
214
  #submit jobs to queue
215
  def submit(self):
 
220
  if len(self.file) == 0:
221
  warning = warning + "You need to select a file."
222
  if len(warning) != 0:
223
+ show_message(
224
+ fontSize=12,
225
+ icon=QtWidgets.QMessageBox.Icon.Critical,
226
+ title="Required Information",
227
+ message=warning
228
+ )
 
229
  return
 
230
  if len(self.strainName.text()) == 0:
231
  warning = warning + "\nIt is recommended to include the organism's subspecies/strain."
232
  if len(self.orgCode.text()) == 0:
 
244
  if msgBox.result() == QtWidgets.QMessageBox.No:
245
  return
246
 
 
 
247
  #endo, pam, repeats, directionality, five length, seed length, three length, orgcode, output path, CASPERinfo path, fna path, orgName, notes, on target matrix
248
  args = self.Endos[self.comboBoxEndo.currentText()][0]
249
  args += " " + self.Endos[self.comboBoxEndo.currentText()][1]
 
281
 
282
  tmp = self.orgName.text()+ " " + self.strainName.text() + " " + self.Endos[self.comboBoxEndo.currentText()][0] + " " + self.orgCode.text()
283
  if tmp in self.check_strings:
284
+ show_message(
285
+ fontSize=12,
286
+ icon=QtWidgets.QMessageBox.Icon.Critical,
287
+ title="Duplicate Entry",
288
+ message="You have submitted a duplicate entry. Consider changing the organism code or strain name to differentiate closely related strains."
289
+ )
 
 
290
  return
291
  name = self.orgCode.text() + "_" + str(self.Endos[self.comboBoxEndo.currentText()][0])
292
  rowPosition = self.job_Table.rowCount()
 
297
  self.check_strings.append(tmp)
298
  self.JobsQueue.append(args)
299
  except Exception as e:
300
+ show_error("Error in submit() in New Genome.", e)
 
 
 
 
 
 
 
 
 
 
 
 
301
 
302
  #fill the endo dropdown
303
  def fillEndo(self):
 
344
  #reconnect signal
345
  self.comboBoxEndo.currentIndexChanged.connect(self.changeEndos)
346
  except Exception as e:
347
+ show_error("Error in fillEndo() in New Genome.", e)
 
 
 
 
 
 
 
 
 
 
 
 
348
 
349
  #event handler for endo changing - update endo length data
350
  def changeEndos(self):
 
354
  self.five_length.setText(self.Endos[key][2])
355
  self.three_length.setText(self.Endos[key][4])
356
  except Exception as e:
357
+ show_error("Error in changeEndos() in New Genome.", e)
 
 
 
 
 
 
 
 
 
 
 
 
358
 
359
  #check if endo is 3' or 5'
360
  def endo_settings(self):
 
365
  elif int(self.seqTrans.endo_info[self.Endos[self.comboBoxEndo.currentText()][0]][3]) == 5:
366
  self.pamBox.setChecked(1)
367
  except Exception as e:
368
+ show_error("Error in endo_settings() in New Genome.", e)
 
 
 
 
 
 
 
 
 
 
 
 
369
 
370
  #wrapper for running jobs
371
  def run_jobs_wrapper(self):
 
378
  self.indexes.append(index.row())
379
  self.run_job()
380
  except Exception as e:
381
+ show_error("Error in run_jobs_wrapper() in New Genome.", e)
 
 
 
 
 
 
 
 
 
 
 
 
382
 
383
  #run job in queue
384
  def run_job(self):
 
441
  self.process.readyReadStandardOutput.connect(partial(output_stdout, self.process))
442
  self.process.start(program)
443
  else:
444
+ show_message(
445
+ fontSize=12,
446
+ icon=QtWidgets.QMessageBox.Icon.Critical,
447
+ title="No Jobs To Run",
448
+ message="No jobs are in the queue to run. Please add a job before running."
449
+ )
 
 
450
  except Exception as e:
451
+ show_error("Error in run_job() in New Genome.", e)
 
 
 
 
 
 
 
 
 
 
 
 
452
 
453
  #even handler for when jobs finish execution
454
  def upon_process_finishing(self):
 
464
  self.run_job()
465
  else:
466
  #prompt user if they want to analyze their new files
467
+ center_ui(self.goToPrompt)
468
  self.goToPrompt.show()
469
  self.goToPrompt.activateWindow()
470
  except Exception as e:
471
+ show_error("Error in upon_process_finishing() in New Genome.", e)
 
 
 
 
 
 
 
 
 
 
 
 
472
 
473
  #clear the job table
474
  def clear_all(self):
 
489
  self.progressBar.setValue(0)
490
  self.first = False
491
  except Exception as e:
492
+ show_error("Error in clear_all() in New Genome.", e)
 
 
 
 
 
 
 
 
 
 
 
 
493
 
494
  #reset the whole form
495
  def reset(self):
 
503
  self.output_browser.setText("Waiting for program initiation...")
504
  self.file = ""
505
  except Exception as e:
506
+ show_error("Error in reset() in New Genome.", e)
507
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
508
  #event handler for user wanting to close the window
509
  def closeEvent(self, event):
510
  try:
 
534
  else:
535
  self.exit = False
536
  event.accept()
 
537
  else:
538
  self.process.kill()
539
  self.clear_all()
 
553
  GlobalSettings.mainWindow.show()
554
  event.accept()
555
  except Exception as e:
556
+ show_error("Error in closeEvent() in New Genome.", e)
 
 
 
 
 
 
 
 
 
 
 
 
557
 
558
  #event handler for user wanting to go to Main once jobs complete
559
  def continue_to_main(self):
 
578
  msgBox.addButton(QtWidgets.QMessageBox.StandardButton.No)
579
  msgBox.exec()
580
 
 
581
  if (msgBox.result() == QtWidgets.QMessageBox.Yes):
582
  self.exit = True
583
  self.close()
 
584
  else:
585
  self.process.kill()
586
  self.clear_all()
 
601
  GlobalSettings.mainWindow.show()
602
  self.hide()
603
  except Exception as e:
604
+ show_error("Error in continue_to_main() in New Genome.", e)
 
 
 
 
 
 
 
 
 
 
 
 
605
 
606
  #event handler for user wanting to go to multi-targeting once jobs complete
607
  def continue_to_MT(self):
 
653
  GlobalSettings.MTWin.show()
654
  self.hide()
655
  except Exception as e:
656
+ show_error("Error in continue_to_MT() in New Genome.", e)
 
 
 
 
 
 
 
 
 
 
 
 
657
 
658
  #event handler for user wanting to go to population analysis once jobs complete
659
  def continue_to_pop(self):
 
702
  GlobalSettings.pop_Analysis.show()
703
  self.hide()
704
  except Exception as e:
705
+ show_error("Error in continue_to_pop() in New Genome.", e)
 
 
 
 
 
 
 
 
 
 
 
 
views/StartupWindow.py ADDED
@@ -0,0 +1,255 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import platform
3
+ import traceback
4
+ import logging
5
+ from PyQt5 import QtGui, QtWidgets, QtCore, uic, Qt
6
+ import models.GlobalSettings as GlobalSettings
7
+ from utils.ui import show_message, show_error, scale_ui
8
+
9
+ logger = GlobalSettings.logger
10
+
11
+ class StartupWindow(QtWidgets.QMainWindow):
12
+ def __init__(self):
13
+ try:
14
+ super(StartupWindow, self).__init__()
15
+ try:
16
+ uic.loadUi(GlobalSettings.appdir + 'ui/startupCASPER.ui', self)
17
+ self.setWindowIcon(QtGui.QIcon(GlobalSettings.appdir + "cas9image.png"))
18
+ except Exception as e:
19
+ show_error("Unable to load UX files for Startup Window.", e)
20
+
21
+ #set "Main" button to be the default highlighted button on startup
22
+ self.goToMain.setDefault(True)
23
+
24
+ #get current directory, and update based on current operating system
25
+ self.currentDirectory = os.getcwd()
26
+ self.databaseDirectory = self.loadDatabaseDirectory()
27
+ GlobalSettings.CSPR_DB = self.databaseDirectory
28
+ if platform.system() == "Windows":
29
+ GlobalSettings.CSPR_DB = GlobalSettings.CSPR_DB.replace("/","\\")
30
+ else:
31
+ GlobalSettings.CSPR_DB = GlobalSettings.CSPR_DB.replace("\\","/")
32
+
33
+ #setup event handlers for startup buttons
34
+ self.currentDirText.setText(self.databaseDirectory)
35
+ self.changeDir.clicked.connect(self.change_directory)
36
+ self.goToMain.clicked.connect(self.launchMainWindow)
37
+ self.goToNewGenome.clicked.connect(self.launchNewGenome)
38
+
39
+ self.setWindowTitle("CASPER")
40
+ self.setWindowIcon(Qt.QIcon(GlobalSettings.appdir + "cas9image.ico"))
41
+
42
+ scale_ui(self, custom_scale_width=1150, custom_scale_height=650)
43
+
44
+ except Exception as e:
45
+ show_error("Error initializing StartupWindow class.", e)
46
+
47
+ #event handler for user clicking the "Change..." button - used for changing CASPER database directory
48
+ def change_directory(self):
49
+ try:
50
+ # Launch OS file browser
51
+ newDirectory = QtWidgets.QFileDialog.getExistingDirectory(
52
+ self, "Open a folder...", self.databaseDirectory, QtWidgets.QFileDialog.ShowDirsOnly)
53
+
54
+ # Check if selected path is a directory in the system
55
+ if not os.path.isdir(newDirectory):
56
+ show_message(
57
+ fontSize=self.fontSize,
58
+ icon=QtWidgets.QMessageBox.Icon.Critical,
59
+ title="Not a directory",
60
+ message="The directory you selected does not exist.",
61
+ )
62
+ return
63
+
64
+ # Ensure directory contains correct filepath format based on OS
65
+ newDirectory = newDirectory.replace("/", "\\") if platform.system() == "Windows" else newDirectory.replace("\\", "/")
66
+
67
+ # Update text edit showing the current selected database directory
68
+ self.currentDirText.setText(newDirectory)
69
+
70
+ # Update CASPER database directories
71
+ self.databaseDirectory = newDirectory
72
+ GlobalSettings.CSPR_DB = newDirectory
73
+
74
+ except Exception as e:
75
+ show_error("change_directory() in startup window", e)
76
+
77
+ #function for loading the default database directory specified in CASPERinfo
78
+ #returns: default database parsed from CASPERinfo
79
+ def loadDatabaseDirectory(self):
80
+ casperInfoPath = os.path.join(GlobalSettings.appdir, "CASPERinfo")
81
+ defaultDirectory = "Where would you like to store CASPER database files?" # Default message if directory not found
82
+
83
+ try:
84
+ with open(casperInfoPath, 'r') as file:
85
+ for line in file:
86
+ if 'DIRECTORY:' in line:
87
+ defaultDirectory = line.strip().replace("DIRECTORY:", "").strip()
88
+ break
89
+
90
+ # Ensure the directory path is formatted correctly based on the operating system
91
+ if platform.system() == "Windows":
92
+ defaultDirectory = defaultDirectory.replace("/", "\\")
93
+ else:
94
+ defaultDirectory = defaultDirectory.replace("\\", "/")
95
+
96
+ logger.debug("Successfully parsed CASPERinfo for default database directory.")
97
+ return defaultDirectory
98
+
99
+ except Exception as e:
100
+ show_error(f"Error reading {casperInfoPath}: {e}", e)
101
+
102
+ return defaultDirectory
103
+
104
+ #function for saving the currently selected database directory to CASPERinfo to be the new default value on startup
105
+ def saveDatabaseDirectory(self):
106
+ try:
107
+ #variable to hold the CASPERinfo data with new default directory change
108
+ CASPERInfoNewData = ""
109
+
110
+ #new default directory string for CASPERinfo
111
+ newDefaultDirectory = "DIRECTORY:" + str(self.databaseDirectory)
112
+
113
+ #open CASPERinfo file to read in the files data and add in new change
114
+ try:
115
+ CASPERInfo = open(GlobalSettings.appdir + "CASPERinfo", 'r+')
116
+ CASPERinfoData = CASPERInfo.read()
117
+ CASPERinfoData = CASPERinfoData.split('\n')
118
+ for line in CASPERinfoData:
119
+ #if directory line found, use new default directory string instead
120
+ if 'DIRECTORY:' in line:
121
+ CASPERInfoNewData = CASPERInfoNewData + "\n" + newDefaultDirectory
122
+ else:
123
+ CASPERInfoNewData = CASPERInfoNewData + "\n" + line
124
+ CASPERInfoNewData = CASPERInfoNewData[1:]
125
+
126
+ #close CASPERinfo
127
+ CASPERInfo.close()
128
+
129
+ #re-open the file and re-write it with current changes
130
+ CASPERInfo = open(GlobalSettings.appdir + "CASPERinfo", 'w+')
131
+ CASPERInfo.write(CASPERInfoNewData)
132
+ CASPERInfo.close()
133
+ logger.debug("Successfully updated CASPERinfo with new default database directory.")
134
+ except Exception as e:
135
+ show_error("Unable to write to CASPERinfo file to update database directory.", e)
136
+ except Exception as e:
137
+ show_error("Error in saveDatabaseDirectory() in startup window.", e)
138
+
139
+ # Event handler for user clicking the "New Genome" button - used for launching New Genome
140
+ def launchNewGenome(self):
141
+ try:
142
+ # Make sure database directory variable is up-to-date based on what the user has in the text edit
143
+ self.databaseDirectory = str(self.currentDirText.text())
144
+
145
+ if not os.path.isdir(self.databaseDirectory):
146
+ show_message(
147
+ fontSize=12,
148
+ icon=QtWidgets.QMessageBox.Icon.Critical,
149
+ title="Not a directory",
150
+ message="The directory you selected does not exist.",
151
+ )
152
+ return
153
+
154
+ # Change directories to the specified database directory provided
155
+ os.chdir(self.databaseDirectory)
156
+
157
+ # Write out the database directory to CASPERinfo to be the new default loaded value
158
+ self.saveDatabaseDirectory()
159
+
160
+ # Update global database variable
161
+ GlobalSettings.CSPR_DB = self.databaseDirectory
162
+
163
+ # Create app directories
164
+ initialize_app_directories()
165
+
166
+ # Launch New Genome window
167
+ self.launch_new_genome()
168
+
169
+ self.close()
170
+ except Exception as e:
171
+ show_error("launchNewGenome() in startup window", e)
172
+
173
+ def launch_new_genome(self):
174
+ try:
175
+ GlobalSettings.mainWindow.launch_newGenome()
176
+ logger.debug("Successfully initialized New Genome in startup window.")
177
+ except Exception as e:
178
+ show_error("launch_new_genome() in startup window", e)
179
+
180
+ # Event handler for user clicking "Main Program" button - used to launch Main Window
181
+ def launchMainWindow(self):
182
+ try:
183
+ # Make sure database directory variable is up-to-date based on what the user has in the text edit
184
+ self.databaseDirectory = str(self.currentDirText.text())
185
+
186
+ # Make sure the path is a valid path before launching New Genome
187
+ if not os.path.isdir(self.databaseDirectory):
188
+ show_message(
189
+ fontSize=12,
190
+ icon=QtWidgets.QMessageBox.Icon.Critical,
191
+ title="Not a directory",
192
+ message="The directory you selected does not exist.",
193
+ )
194
+ return
195
+
196
+ # Check if database directory has CSPR files in it
197
+ if not any(file.endswith(".cspr") for file in os.listdir(self.databaseDirectory)):
198
+ show_message(
199
+ fontSize=12,
200
+ icon=QtWidgets.QMessageBox.Icon.Critical,
201
+ title="Directory is invalid!",
202
+ message="You must select a directory with CSPR Files!",
203
+ )
204
+ return
205
+
206
+ # Change directory to database directory
207
+ os.chdir(self.databaseDirectory)
208
+
209
+ # Update database directory global variable
210
+ GlobalSettings.CSPR_DB = self.databaseDirectory
211
+
212
+ # Save database directory to CASPERinfo
213
+ self.saveDatabaseDirectory()
214
+
215
+ initialize_app_directories()
216
+
217
+ # Fill in organism/endo/annotation dropdown information for main, mulit-targeting, and populatin analysis
218
+ self.load_dropdown_data()
219
+
220
+ # Show main window
221
+ if GlobalSettings.mainWindow.first_show:
222
+ GlobalSettings.mainWindow.first_show = False
223
+ GlobalSettings.mainWindow.show()
224
+ self.close()
225
+
226
+ except Exception as e:
227
+ show_error("launchMainWindow() in startup window", e)
228
+
229
+ def load_dropdown_data(self):
230
+ try:
231
+ GlobalSettings.mainWindow.getData()
232
+ GlobalSettings.mainWindow.fill_annotation_dropdown()
233
+ logger.debug("Successfully loaded organism/endo/annotation drop down information in Main.")
234
+ except Exception as e:
235
+ show_error("load_dropdown_data() in Main", e)
236
+
237
+ try:
238
+ GlobalSettings.MTWin.launch()
239
+ logger.debug("Successfully loaded organism/endo drop down information in Multi-targeting.")
240
+ except Exception as e:
241
+ show_error("load_dropdown_data() in Multi-targeting", e)
242
+
243
+ try:
244
+ GlobalSettings.pop_Analysis.launch()
245
+ logger.debug("Successfully loaded organism/endo drop down information in Population Analysis.")
246
+ except Exception as e:
247
+ show_error("load_dropdown_data() in Population Analysis", e)
248
+
249
+ def initialize_app_directories():
250
+ required_dirs = ["FNA", "GBFF"]
251
+ for directory in required_dirs:
252
+ path = os.path.join(GlobalSettings.CSPR_DB, directory)
253
+ if not os.path.exists(path):
254
+ os.makedirs(path, exist_ok=True)
255
+ logging.info(f"Directory created: {path}")
annotation_functions.py → views/annotation_functions.py RENAMED
File without changes
views/closingWin.py ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import models.GlobalSettings as GlobalSettings
2
+ import os
3
+ from PyQt5 import QtWidgets, Qt, uic
4
+ import traceback
5
+ import math
6
+ from utils.ui import show_error, scale_ui
7
+
8
+ logger = GlobalSettings.logger
9
+
10
+ ###########################################################
11
+ # closingWindow: this class is a little window where the user can select which files they want to delete
12
+ # Once they hit 'submit' it will delete all of the files selected, and close the program.
13
+ # If no files are selected, the program closes and no files are deleted
14
+ # Inputs are taking from the user (selecting files to delete and hitting submit), as well as GlobalSettings for the files in CSPR_DB
15
+ # Outputs are the files are deleting, and the program is closed
16
+ ###########################################################
17
+ class closingWindow(QtWidgets.QMainWindow):
18
+ def __init__(self):
19
+ try:
20
+ super(closingWindow, self).__init__()
21
+ uic.loadUi(GlobalSettings.appdir + "ui/closing_window.ui", self)
22
+ self.setWindowTitle("Delete Files")
23
+ self.setWindowIcon(Qt.QIcon(GlobalSettings.appdir + "cas9image.ico"))
24
+
25
+ # Button
26
+ self.submit_button.clicked.connect(self.submit_and_close)
27
+
28
+ # Table
29
+ self.files_table.setColumnCount(1)
30
+ self.files_table.setShowGrid(True)
31
+ self.files_table.setHorizontalHeaderLabels("File Name;".split(";"))
32
+ self.files_table.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
33
+ self.files_table.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
34
+ self.files_table.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
35
+
36
+
37
+ scale_ui(self, custom_scale_width=400, custom_scale_height=300)
38
+
39
+
40
+ except Exception as e:
41
+ show_error("Error initializing closingWindow class.", e)
42
+
43
+ # this function will delete selected files, and then close the program
44
+ def submit_and_close(self):
45
+ try:
46
+ # loop through the whole table
47
+ for i in range(self.files_table.rowCount()):
48
+ tabWidget = self.files_table.item(i, 0)
49
+
50
+ # if that specific tab is selected, delete it. otherwise do nothing
51
+ if tabWidget.isSelected():
52
+ os.remove(tabWidget.text())
53
+ self.close()
54
+ except Exception as e:
55
+ show_error("Error in sumbit_and_close() in closing window.", e)
56
+
57
+ # this function gets all of the files from the CSPR_DB and puts them all into the table
58
+ def get_files(self):
59
+ try:
60
+ loopCount = 0
61
+ # get the file names from CSPR_DB
62
+ files_names = os.listdir(GlobalSettings.CSPR_DB)
63
+ files_names.sort(key=str.lower)
64
+ self.files_table.setRowCount(len(files_names))
65
+
66
+ # loop through and add them to the table
67
+ for file in files_names:
68
+ tabWidget = QtWidgets.QTableWidgetItem(file)
69
+ self.files_table.setItem(loopCount, 0, tabWidget)
70
+ loopCount += 1
71
+ self.files_table.resizeColumnsToContents()
72
+ except Exception as e:
73
+ show_error("Error in get_files() in closing window.", e)
export_tool.py → views/export_tool.py RENAMED
@@ -1,27 +1,23 @@
1
- import GlobalSettings
2
- from Algorithms import get_table_headers
3
  import os
4
  from PyQt5 import QtWidgets, Qt, uic, QtCore, QtGui
5
  import platform
6
  import traceback
7
  import math
 
8
 
9
- #global logger
10
  logger = GlobalSettings.logger
11
 
12
- # Class: export_tool
13
  # This class opens a window for the user to select where they want the CSV file exported to, and the name of the file
14
  # It takes the highlighted data from the Results page, and creates a CSV file from that
15
  class export_tool(QtWidgets.QMainWindow):
16
- # init function. Sets all of the buttons
17
  def __init__(self):
18
  try:
19
- # qt stuff
20
  super(export_tool, self).__init__()
21
- uic.loadUi(GlobalSettings.appdir + 'export_tool.ui', self)
22
  self.setWindowIcon(Qt.QIcon(GlobalSettings.appdir + "cas9image.ico"))
23
 
24
- # button connections
25
  self.browse_button.clicked.connect(self.browseForFolder)
26
  self.cancel_button.clicked.connect(self.cancel_function)
27
  self.export_button.clicked.connect(self.export_function)
@@ -43,7 +39,6 @@ class export_tool(QtWidgets.QMainWindow):
43
  font: bold 14pt 'Arial';} """
44
  self.gRNA_Options.setStyleSheet(groupbox_style)
45
 
46
- # variables
47
  self.location = self.fileLocation_line_edit.text()
48
  self.selected_table_items = []
49
  self.window = ""
@@ -52,111 +47,10 @@ class export_tool(QtWidgets.QMainWindow):
52
  self.gene_name = False
53
 
54
  self.setWindowTitle("Export to CSV")
55
- self.scaleUI()
56
 
57
  except Exception as e:
58
- logger.critical("Error initializing export_tool class.")
59
- logger.critical(e)
60
- logger.critical(traceback.format_exc())
61
- msgBox = QtWidgets.QMessageBox()
62
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
63
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
64
- msgBox.setWindowTitle("Fatal Error")
65
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
66
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
67
- msgBox.exec()
68
-
69
- exit(-1)
70
-
71
- #scale UI based on current screen
72
- def scaleUI(self):
73
- try:
74
- self.repaint()
75
- QtWidgets.QApplication.processEvents()
76
-
77
- screen = QtWidgets.QApplication.desktop().screenNumber(QtWidgets.QApplication.desktop().cursor().pos())
78
- screen = QtWidgets.QApplication.screens()[screen]
79
- dpi = screen.physicalDotsPerInch()
80
- width = screen.geometry().width()
81
- height = screen.geometry().height()
82
-
83
- # font scaling
84
- fontSize = 12
85
- self.fontSize = fontSize
86
- self.centralWidget().setStyleSheet("font: " + str(fontSize) + "pt 'Arial';")
87
-
88
- self.adjustSize()
89
-
90
- currentWidth = self.size().width()
91
- currentHeight = self.size().height()
92
-
93
- # window scaling
94
- # 1920x1080 => 1150x650
95
- scaledWidth = int((width * 650) / 1920)
96
- scaledHeight = int((height * 200) / 1080)
97
-
98
- if scaledHeight < currentHeight:
99
- scaledHeight = currentHeight
100
- if scaledWidth < currentWidth:
101
- scaledWidth = currentWidth
102
-
103
- screen = QtWidgets.QApplication.desktop().screenNumber(QtWidgets.QApplication.desktop().cursor().pos())
104
- centerPoint = QtWidgets.QApplication.desktop().screenGeometry(screen).center()
105
- x = centerPoint.x()
106
- y = centerPoint.y()
107
- x = x - (math.ceil(scaledWidth / 2))
108
- y = y - (math.ceil(scaledHeight / 2))
109
- self.setGeometry(x, y, scaledWidth, scaledHeight)
110
-
111
- self.repaint()
112
- QtWidgets.QApplication.processEvents()
113
-
114
- except Exception as e:
115
- logger.critical("Error in scaleUI() in export_tool.")
116
- logger.critical(e)
117
- logger.critical(traceback.format_exc())
118
- msgBox = QtWidgets.QMessageBox()
119
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
120
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
121
- msgBox.setWindowTitle("Fatal Error")
122
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
123
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
124
- msgBox.exec()
125
-
126
- exit(-1)
127
-
128
- #center UI on current screen
129
- def centerUI(self):
130
- try:
131
- self.repaint()
132
- QtWidgets.QApplication.processEvents()
133
-
134
- #center window on current screen
135
- width = self.width()
136
- height = self.height()
137
- screen = QtWidgets.QApplication.desktop().screenNumber(QtWidgets.QApplication.desktop().cursor().pos())
138
- centerPoint = QtWidgets.QApplication.desktop().screenGeometry(screen).center()
139
- x = centerPoint.x()
140
- y = centerPoint.y()
141
- x = x - (math.ceil(width / 2))
142
- y = y - (math.ceil(height / 2))
143
- self.setGeometry(x, y, width, height)
144
-
145
- self.repaint()
146
- QtWidgets.QApplication.processEvents()
147
- except Exception as e:
148
- logger.critical("Error in centerUI() in export_tool.")
149
- logger.critical(e)
150
- logger.critical(traceback.format_exc())
151
- msgBox = QtWidgets.QMessageBox()
152
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
153
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
154
- msgBox.setWindowTitle("Fatal Error")
155
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
156
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
157
- msgBox.exec()
158
-
159
- exit(-1)
160
 
161
  # launch function. Called in Results.
162
  # parameter expect: a list of the items selected from the window.
@@ -168,24 +62,12 @@ class export_tool(QtWidgets.QMainWindow):
168
  self.fileLocation_line_edit.setText(GlobalSettings.CSPR_DB + "/")
169
  self.selected_table_items = select_items
170
  self.window = window
171
- self.centerUI()
172
  self.show()
173
  self.activateWindow()
174
  except Exception as e:
175
- logger.critical("Error in launch() in export_tool.")
176
- logger.critical(e)
177
- logger.critical(traceback.format_exc())
178
- msgBox = QtWidgets.QMessageBox()
179
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
180
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
181
- msgBox.setWindowTitle("Fatal Error")
182
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
183
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
184
- msgBox.exec()
185
-
186
- exit(-1)
187
-
188
- # export function
189
  # Takes the path and file name and combines them
190
  # Writes the header line, as well as ever line selected to that file
191
  # calls the cancel function when it's done
@@ -321,48 +203,27 @@ class export_tool(QtWidgets.QMainWindow):
321
  tmp_list.append(item.text())
322
  it += 1
323
  output_data.close()
324
- # catch the permission exception
325
  except PermissionError:
326
- msgBox = QtWidgets.QMessageBox()
327
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
328
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
329
- msgBox.setWindowTitle("File Cannot Open")
330
- msgBox.setText("This file cannot be opened. Please make sure that the file is not opened elsewhere and try again.")
331
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
332
- msgBox.exec()
333
  return
334
 
335
- # catch any other exception
336
  except Exception as e:
337
- print(e)
338
  return
339
 
340
  """ Print "finished" message """
341
- msgBox = QtWidgets.QMessageBox()
342
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
343
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Information)
344
- msgBox.setWindowTitle("Export Complete")
345
- msgBox.setText("Export to %s was successful." % full_path)
346
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
347
- msgBox.exec()
348
 
349
  # close the window
350
  self.cancel_function()
351
-
352
  except Exception as e:
353
- logger.critical("Error in export_function() in export_tool.")
354
- logger.critical(e)
355
- logger.critical(traceback.format_exc())
356
- msgBox = QtWidgets.QMessageBox()
357
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
358
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
359
- msgBox.setWindowTitle("Fatal Error")
360
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
361
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
362
- msgBox.exec()
363
-
364
- exit(-1)
365
-
366
  # Resets everything to the init funciton
367
  # then closes the window
368
  def cancel_function(self):
@@ -375,19 +236,8 @@ class export_tool(QtWidgets.QMainWindow):
375
  self.location = ""
376
  self.hide()
377
  except Exception as e:
378
- logger.critical("Error in cancel_function() in export_tool.")
379
- logger.critical(e)
380
- logger.critical(traceback.format_exc())
381
- msgBox = QtWidgets.QMessageBox()
382
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
383
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
384
- msgBox.setWindowTitle("Fatal Error")
385
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
386
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
387
- msgBox.exec()
388
-
389
- exit(-1)
390
-
391
  # browse for folder function
392
  # allows user to browse for a folder where to store the CSV file
393
  def browseForFolder(self):
@@ -399,7 +249,6 @@ class export_tool(QtWidgets.QMainWindow):
399
  if(os.path.isdir(mydir) == False):
400
  return
401
 
402
-
403
  if platform.system() == "Windows":
404
  self.fileLocation_line_edit.setText(mydir + "\\")
405
  self.location = mydir + "\\"
@@ -407,15 +256,4 @@ class export_tool(QtWidgets.QMainWindow):
407
  self.fileLocation_line_edit.setText(mydir + "/")
408
  self.location = mydir + "/"
409
  except Exception as e:
410
- logger.critical("Error in browseForFolder() in export_tool.")
411
- logger.critical(e)
412
- logger.critical(traceback.format_exc())
413
- msgBox = QtWidgets.QMessageBox()
414
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
415
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
416
- msgBox.setWindowTitle("Fatal Error")
417
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
418
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
419
- msgBox.exec()
420
-
421
- exit(-1)
 
1
+ import models.GlobalSettings as GlobalSettings
2
+ from utils.Algorithms import get_table_headers
3
  import os
4
  from PyQt5 import QtWidgets, Qt, uic, QtCore, QtGui
5
  import platform
6
  import traceback
7
  import math
8
+ from utils.ui import show_message, show_error, scale_ui, center_ui
9
 
 
10
  logger = GlobalSettings.logger
11
 
 
12
  # This class opens a window for the user to select where they want the CSV file exported to, and the name of the file
13
  # It takes the highlighted data from the Results page, and creates a CSV file from that
14
  class export_tool(QtWidgets.QMainWindow):
 
15
  def __init__(self):
16
  try:
 
17
  super(export_tool, self).__init__()
18
+ uic.loadUi(GlobalSettings.appdir + 'ui/export_tool.ui', self)
19
  self.setWindowIcon(Qt.QIcon(GlobalSettings.appdir + "cas9image.ico"))
20
 
 
21
  self.browse_button.clicked.connect(self.browseForFolder)
22
  self.cancel_button.clicked.connect(self.cancel_function)
23
  self.export_button.clicked.connect(self.export_function)
 
39
  font: bold 14pt 'Arial';} """
40
  self.gRNA_Options.setStyleSheet(groupbox_style)
41
 
 
42
  self.location = self.fileLocation_line_edit.text()
43
  self.selected_table_items = []
44
  self.window = ""
 
47
  self.gene_name = False
48
 
49
  self.setWindowTitle("Export to CSV")
50
+ scale_ui(self, custom_scale_width=650, custom_scale_height=200)
51
 
52
  except Exception as e:
53
+ show_error("Error initializing export_tool class.", e)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
 
55
  # launch function. Called in Results.
56
  # parameter expect: a list of the items selected from the window.
 
62
  self.fileLocation_line_edit.setText(GlobalSettings.CSPR_DB + "/")
63
  self.selected_table_items = select_items
64
  self.window = window
65
+ center_ui(self)
66
  self.show()
67
  self.activateWindow()
68
  except Exception as e:
69
+ show_error("Error in launch() in export_tool.", e)
70
+
 
 
 
 
 
 
 
 
 
 
 
 
71
  # Takes the path and file name and combines them
72
  # Writes the header line, as well as ever line selected to that file
73
  # calls the cancel function when it's done
 
203
  tmp_list.append(item.text())
204
  it += 1
205
  output_data.close()
 
206
  except PermissionError:
207
+ show_error("This file cannot be opened. Please make sure that the file is not opened elsewhere and try again.", e)
 
 
 
 
 
 
208
  return
209
 
 
210
  except Exception as e:
211
+ show_error("Error in export_function() in export_tool.", e)
212
  return
213
 
214
  """ Print "finished" message """
215
+ show_message(
216
+ fontSize=12,
217
+ icon=QtWidgets.QMessageBox.Icon.Information,
218
+ title="Export Complete",
219
+ message=f"Export to {full_path} was successful."
220
+ )
 
221
 
222
  # close the window
223
  self.cancel_function()
 
224
  except Exception as e:
225
+ show_error("Error in export_function() in export_tool.", e)
226
+
 
 
 
 
 
 
 
 
 
 
 
227
  # Resets everything to the init funciton
228
  # then closes the window
229
  def cancel_function(self):
 
236
  self.location = ""
237
  self.hide()
238
  except Exception as e:
239
+ show_error("Error in cancel_function() in export_tool.", e)
240
+
 
 
 
 
 
 
 
 
 
 
 
241
  # browse for folder function
242
  # allows user to browse for a folder where to store the CSV file
243
  def browseForFolder(self):
 
249
  if(os.path.isdir(mydir) == False):
250
  return
251
 
 
252
  if platform.system() == "Windows":
253
  self.fileLocation_line_edit.setText(mydir + "\\")
254
  self.location = mydir + "\\"
 
256
  self.fileLocation_line_edit.setText(mydir + "/")
257
  self.location = mydir + "/"
258
  except Exception as e:
259
+ show_error("Error in browseForFolder() in export_tool.", e)
 
 
 
 
 
 
 
 
 
 
 
generateLib.py → views/generateLib.py RENAMED
@@ -1,29 +1,26 @@
1
- import GlobalSettings
2
  import os
3
  from PyQt5 import QtWidgets, Qt, uic, QtCore
4
  from functools import partial
5
- from CSPRparser import CSPRparser
6
  import re
7
  import platform
8
  import traceback
9
  import math
10
- from annotation_functions import *
 
11
 
12
- #global logger
13
  logger = GlobalSettings.logger
14
 
15
- # Class Name: genLibrary
16
  # this class is a window that allows the user to select the settings for Generate Library
17
  # When the user clicks Generate Library, it goes ahead and gets the Annotation Data needed
18
  # Then the user can select the settings they want, and then hit submit.
19
  # It creates a txt file with the data
20
  class genLibrary(QtWidgets.QMainWindow):
21
-
22
  def __init__(self):
23
  try:
24
- # qt stuff
25
  super(genLibrary, self).__init__()
26
- uic.loadUi(GlobalSettings.appdir + 'generate_library.ui', self)
27
  self.setWindowTitle('Generate Library')
28
  self.setWindowIcon(Qt.QIcon(GlobalSettings.appdir + 'cas9image.ico'))
29
 
@@ -40,14 +37,11 @@ class genLibrary(QtWidgets.QMainWindow):
40
  self.Step3.setStyleSheet(groupbox_style.replace("Step1", "Step3"))
41
  self.Step4.setStyleSheet(groupbox_style.replace("Step1", "Step4"))
42
 
43
-
44
- # button connections
45
  self.cancel_button.clicked.connect(self.cancel_function)
46
  self.BrowseButton.clicked.connect(self.browse_function)
47
  self.submit_button.clicked.connect(self.submit_data)
48
  self.progressBar.setValue(0)
49
 
50
- # variables
51
  self.anno_data = dict()
52
  self.kegg_nonKegg = ''
53
  self.gen_lib_dict = dict()
@@ -65,118 +59,12 @@ class genLibrary(QtWidgets.QMainWindow):
65
  # set the numbers for the minOn combo box
66
  for i in range(19, 70):
67
  self.minON_comboBox.addItem(str(i + 1))
68
-
69
- #scale UI
70
- self.scaleUI()
71
-
72
- except Exception as e:
73
- logger.critical("Error initializing generate library class.")
74
- logger.critical(e)
75
- logger.critical(traceback.format_exc())
76
- msgBox = QtWidgets.QMessageBox()
77
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
78
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
79
- msgBox.setWindowTitle("Fatal Error")
80
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
81
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
82
- msgBox.exec()
83
-
84
- exit(-1)
85
-
86
- #scale UI based on current screen
87
- def scaleUI(self):
88
- try:
89
- self.repaint()
90
- QtWidgets.QApplication.processEvents()
91
-
92
- screen = QtWidgets.QApplication.desktop().screenNumber(QtWidgets.QApplication.desktop().cursor().pos())
93
- screen = QtWidgets.QApplication.screens()[screen]
94
- dpi = screen.physicalDotsPerInch()
95
- width = screen.geometry().width()
96
- height = screen.geometry().height()
97
-
98
- # font scaling
99
- fontSize = 12
100
- self.fontSize = fontSize
101
- self.centralWidget().setStyleSheet("font: " + str(fontSize) + "pt 'Arial';")
102
-
103
- #scale title
104
- fontSize = 30
105
- self.label.setStyleSheet("font: bold " + str(fontSize) + "pt 'Arial';")
106
-
107
- self.adjustSize()
108
-
109
- currentWidth = self.size().width()
110
- currentHeight = self.size().height()
111
-
112
- # window scaling
113
- # 1920x1080 => 800x650
114
- scaledWidth = int((width * 950) / 1920)
115
- scaledHeight = int((height * 500) / 1080)
116
-
117
- if scaledHeight < currentHeight:
118
- scaledHeight = currentHeight
119
- if scaledWidth < currentWidth:
120
- scaledWidth = currentWidth
121
-
122
- screen = QtWidgets.QApplication.desktop().screenNumber(QtWidgets.QApplication.desktop().cursor().pos())
123
- centerPoint = QtWidgets.QApplication.desktop().screenGeometry(screen).center()
124
- x = centerPoint.x()
125
- y = centerPoint.y()
126
- x = x - (math.ceil(scaledWidth / 2))
127
- y = y - (math.ceil(scaledHeight / 2))
128
- self.setGeometry(x, y, scaledWidth, scaledHeight)
129
-
130
- self.repaint()
131
- QtWidgets.QApplication.processEvents()
132
 
133
  except Exception as e:
134
- logger.critical("Error in scaleUI() in generate library.")
135
- logger.critical(e)
136
- logger.critical(traceback.format_exc())
137
- msgBox = QtWidgets.QMessageBox()
138
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
139
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
140
- msgBox.setWindowTitle("Fatal Error")
141
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
142
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
143
- msgBox.exec()
144
-
145
- exit(-1)
146
-
147
- #center UI on current screen
148
- def centerUI(self):
149
- try:
150
- self.repaint()
151
- QtWidgets.QApplication.processEvents()
152
-
153
- #center window on current screen
154
- width = self.width()
155
- height = self.height()
156
- screen = QtWidgets.QApplication.desktop().screenNumber(QtWidgets.QApplication.desktop().cursor().pos())
157
- centerPoint = QtWidgets.QApplication.desktop().screenGeometry(screen).center()
158
- x = centerPoint.x()
159
- y = centerPoint.y()
160
- x = x - (math.ceil(width / 2))
161
- y = y - (math.ceil(height / 2))
162
- self.setGeometry(x, y, width, height)
163
-
164
- self.repaint()
165
- QtWidgets.QApplication.processEvents()
166
- except Exception as e:
167
- logger.critical("Error in centerUI() in generate library.")
168
- logger.critical(e)
169
- logger.critical(traceback.format_exc())
170
- msgBox = QtWidgets.QMessageBox()
171
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
172
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
173
- msgBox.setWindowTitle("Fatal Error")
174
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
175
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
176
- msgBox.exec()
177
-
178
- exit(-1)
179
-
180
  # this function launches the window
181
  # Parameters:
182
  # annotation_data: a dictionary that has the data for the annotations searched for
@@ -192,7 +80,6 @@ class genLibrary(QtWidgets.QMainWindow):
192
  self.process = QtCore.QProcess()
193
  self.parser.fileName = org_file
194
 
195
-
196
  # setting the path and file name fields
197
  index1 = self.cspr_file.find('.')
198
  if platform.system() == "Windows":
@@ -215,23 +102,11 @@ class genLibrary(QtWidgets.QMainWindow):
215
  self.cspr_data = self.parser.gen_lib_parser(self.gen_lib_dict, GlobalSettings.mainWindow.endoChoice.currentText())
216
  self.get_endo_data()
217
 
218
- #center UI
219
- self.centerUI()
220
  self.show()
221
  self.activateWindow()
222
  except Exception as e:
223
- logger.critical("Error in launch() in generate library.")
224
- logger.critical(e)
225
- logger.critical(traceback.format_exc())
226
- msgBox = QtWidgets.QMessageBox()
227
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
228
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
229
- msgBox.setWindowTitle("Fatal Error")
230
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
231
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
232
- msgBox.exec()
233
-
234
- exit(-1)
235
 
236
  def get_endo_data(self):
237
  try:
@@ -258,19 +133,8 @@ class genLibrary(QtWidgets.QMainWindow):
258
  break
259
  f.close()
260
  except Exception as e:
261
- logger.critical("Error in get_endo_data() in generate library.")
262
- logger.critical(e)
263
- logger.critical(traceback.format_exc())
264
- msgBox = QtWidgets.QMessageBox()
265
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
266
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
267
- msgBox.setWindowTitle("Fatal Error")
268
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
269
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
270
- msgBox.exec()
271
-
272
- exit(-1)
273
-
274
  # this is here in case the user clicks 'x' instead of cancel. Just calls the cancel function
275
  def closeEvent(self, event):
276
  try:
@@ -282,19 +146,8 @@ class genLibrary(QtWidgets.QMainWindow):
282
  else:
283
  event.accept()
284
  except Exception as e:
285
- logger.critical("Error in closeEvent() in generate library.")
286
- logger.critical(e)
287
- logger.critical(traceback.format_exc())
288
- msgBox = QtWidgets.QMessageBox()
289
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
290
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
291
- msgBox.setWindowTitle("Fatal Error")
292
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
293
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
294
- msgBox.exec()
295
-
296
- exit(-1)
297
-
298
  # this function takes all of the cspr data and compresses it again for off-target usage
299
  def compress_file_off(self):
300
  try:
@@ -314,19 +167,8 @@ class genLibrary(QtWidgets.QMainWindow):
314
  f.write(output + '\n')
315
  f.close()
316
  except Exception as e:
317
- logger.critical("Error in compress_file_off() in generate library.")
318
- logger.critical(e)
319
- logger.critical(traceback.format_exc())
320
- msgBox = QtWidgets.QMessageBox()
321
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
322
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
323
- msgBox.setWindowTitle("Fatal Error")
324
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
325
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
326
- msgBox.exec()
327
-
328
- exit(-1)
329
-
330
  # this function parses the temp_off file, which holds the off-target analysis results
331
  # it also updates each target in the cspr_data dictionary to replace the endo with the target's results in off-target
332
  def parse_off_file(self):
@@ -354,19 +196,8 @@ class genLibrary(QtWidgets.QMainWindow):
354
  tempTuple = (self.cspr_data[gene][i][0], self.cspr_data[gene][i][1], self.cspr_data[gene][i][2], self.cspr_data[gene][i][3], self.cspr_data[gene][i][4], scoreDict[self.cspr_data[gene][i][1]])
355
  self.cspr_data[gene][i] = tempTuple
356
  except Exception as e:
357
- logger.critical("Error in parse_off_file() in generate library.")
358
- logger.critical(e)
359
- logger.critical(traceback.format_exc())
360
- msgBox = QtWidgets.QMessageBox()
361
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
362
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
363
- msgBox.setWindowTitle("Fatal Error")
364
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
365
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
366
- msgBox.exec()
367
-
368
- exit(-1)
369
-
370
  # this function runs the off_target command
371
  # NOTE: some changes may be needed to get it to work with other OS besides windows
372
  def get_offTarget_data(self, num_targets, minScore, spaceValue, output_file, fiveseq):
@@ -385,15 +216,12 @@ class genLibrary(QtWidgets.QMainWindow):
385
  #self.process.kill()
386
  if did_work != -1:
387
  self.cancel_function()
388
- msgBox = QtWidgets.QMessageBox()
389
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
390
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Information)
391
- msgBox.setWindowTitle("Library Generated!")
392
- msgBox.setText(
393
- "CASPER has finished generating your library!")
394
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
395
- msgBox.exec()
396
-
397
  os.remove(GlobalSettings.CSPR_DB + '/off_input.txt')
398
  os.remove(GlobalSettings.CSPR_DB + '/temp_off.txt')
399
 
@@ -473,19 +301,8 @@ class genLibrary(QtWidgets.QMainWindow):
473
  QtCore.QTimer.singleShot(100, partial(self.process.start, cmd))
474
  self.process.finished.connect(finished)
475
  except Exception as e:
476
- logger.critical("Error in get_offTarget_data() in generate library.")
477
- logger.critical(e)
478
- logger.critical(traceback.format_exc())
479
- msgBox = QtWidgets.QMessageBox()
480
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
481
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
482
- msgBox.setWindowTitle("Fatal Error")
483
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
484
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
485
- msgBox.exec()
486
-
487
- exit(-1)
488
-
489
  # submit function
490
  # this function takes all of the input from the window, and calls the generate function
491
  # Still need to add the checks for 5' seq, and the percentage thing
@@ -512,103 +329,78 @@ class genLibrary(QtWidgets.QMainWindow):
512
  elif self.space_line_edit.text().isdigit():
513
  spaceValue = int(self.space_line_edit.text())
514
  elif not self.space_line_edit.text().isdigit():
515
- msgBox = QtWidgets.QMessageBox()
516
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
517
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
518
- msgBox.setWindowTitle("Error")
519
- msgBox.setText(
520
- "Please enter integers only for space between guides.")
521
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
522
- msgBox.exec()
523
-
524
  return
525
  # if space value is more than 200, default to 200
526
  if spaceValue > 200:
527
  spaceValue = 200
528
  elif spaceValue < 0:
529
- msgBox = QtWidgets.QMessageBox()
530
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
531
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
532
- msgBox.setWindowTitle("Error")
533
- msgBox.setText(
534
- "Please enter a space-value that is 0 or greater.")
535
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
536
- msgBox.exec()
537
-
538
  return
539
 
540
  if self.find_off_Checkbox.isChecked():
541
  self.compress_file_off()
542
 
543
-
544
-
545
  # get the fiveprimseq data and error check it
546
  if self.fiveprimeseq.text() != '' and self.fiveprimeseq.text().isalpha():
547
  fiveseq = self.fiveprimeseq.text()
548
  elif self.fiveprimeseq.text() != '' and not self.fiveprimeseq.text().isalpha():
549
- msgBox = QtWidgets.QMessageBox()
550
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
551
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
552
- msgBox.setWindowTitle("Error")
553
- msgBox.setText(
554
- "Please make sure only the letters A, T, G, or C are added into 5' End specificity box.")
555
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
556
- msgBox.exec()
557
-
558
  return
559
 
560
-
561
  # get the targeting range data, and error check it here
562
  if not self.start_target_range.text().isdigit() or not self.end_target_range.text().isdigit():
563
- msgBox = QtWidgets.QMessageBox()
564
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
565
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
566
- msgBox.setWindowTitle("Error")
567
- msgBox.setText(
568
- "Error: Please make sure that the start and end target ranges are numbers only. Please make sure that start is 0 or greater, and end is 100 or less. ")
569
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
570
- msgBox.exec()
571
-
572
  return
573
  elif int(self.start_target_range.text()) >= int(self.end_target_range.text()):
574
- msgBox = QtWidgets.QMessageBox()
575
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
576
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
577
- msgBox.setWindowTitle("Error")
578
- msgBox.setText(
579
- "Please make sure that the start number is always less than the end number")
580
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
581
- msgBox.exec()
582
-
583
  return
584
 
585
-
586
  # if they check Off-Targeting
587
  if self.find_off_Checkbox.isChecked():
588
  # make sure its a digit
589
  if self.maxOFF_comboBox.text() == '' or not self.maxOFF_comboBox.text().isdigit() and '.' not in self.maxOFF_comboBox.text():
590
- msgBox = QtWidgets.QMessageBox()
591
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
592
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
593
- msgBox.setWindowTitle("Error")
594
- msgBox.setText(
595
- "Please enter only numbers for Maximum Off-Target Score. It cannot be left blank")
596
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
597
- msgBox.exec()
598
-
599
  return
600
  else:
601
  # make sure it between 0 and .5
602
  if not 0.0 < float(self.maxOFF_comboBox.text()) <= .5:
603
- msgBox = QtWidgets.QMessageBox()
604
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
605
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
606
- msgBox.setWindowTitle("Error")
607
- msgBox.setText(
608
- "Please enter a max off-target score between 0 and 0.5!")
609
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
610
- msgBox.exec()
611
-
612
  return
613
  # compress the data, and then run off-targeting
614
  self.compress_file_off()
@@ -619,36 +411,21 @@ class genLibrary(QtWidgets.QMainWindow):
619
 
620
  if did_work != -1:
621
  self.cancel_function()
622
- msgBox = QtWidgets.QMessageBox()
623
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
624
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
625
- msgBox.setWindowTitle("Library Generated!")
626
- msgBox.setText(
627
- "CASPER has finished generating your library!")
628
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
629
- msgBox.exec()
630
-
631
  except Exception as e:
632
- logger.critical("Error in submit_data() in generate library.")
633
- logger.critical(e)
634
- logger.critical(traceback.format_exc())
635
- msgBox = QtWidgets.QMessageBox()
636
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
637
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
638
- msgBox.setWindowTitle("Fatal Error")
639
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
640
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
641
- msgBox.exec()
642
-
643
- exit(-1)
644
-
645
- # cancel function
646
  # clears everything and hides the window
647
  def cancel_function(self):
648
  try:
649
  if self.off_target_running:
650
  msgBox = QtWidgets.QMessageBox()
651
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
652
  msgBox.setIcon(QtWidgets.QMessageBox.Icon.Question)
653
  msgBox.setWindowTitle("Off-Targeting is running")
654
  msgBox.setText(
@@ -685,20 +462,8 @@ class genLibrary(QtWidgets.QMainWindow):
685
 
686
  self.hide()
687
  except Exception as e:
688
- logger.critical("Error in cancel_function() in generate library.")
689
- logger.critical(e)
690
- logger.critical(traceback.format_exc())
691
- msgBox = QtWidgets.QMessageBox()
692
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
693
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
694
- msgBox.setWindowTitle("Fatal Error")
695
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
696
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
697
- msgBox.exec()
698
-
699
- exit(-1)
700
-
701
- # browse function
702
  # allows the user to browse for a folder
703
  # stores their selection in the output_path line edit
704
  def browse_function(self):
@@ -718,18 +483,7 @@ class genLibrary(QtWidgets.QMainWindow):
718
  else:
719
  self.output_path.setText(mydir + "/")
720
  except Exception as e:
721
- logger.critical("Error in browse_function() in generate library.")
722
- logger.critical(e)
723
- logger.critical(traceback.format_exc())
724
- msgBox = QtWidgets.QMessageBox()
725
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
726
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
727
- msgBox.setWindowTitle("Fatal Error")
728
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
729
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
730
- msgBox.exec()
731
-
732
- exit(-1)
733
 
734
  # this function builds the dictionary that is used in the generate function
735
  # this is the version that builds it from data from feature_table, gbff, or gff
@@ -745,18 +499,7 @@ class genLibrary(QtWidgets.QMainWindow):
745
  ### Order: chromosome number, gene start, gene end, dir of gene, gene description, gene name/locus tag
746
  self.gen_lib_dict[feature_name] = [chrom,int(feature.location.start),int(feature.location.end),get_strand(feature),get_description(feature),get_name(feature)]
747
  except Exception as e:
748
- logger.critical("Error in build_dict_non_kegg() in generate library.")
749
- logger.critical(e)
750
- logger.critical(traceback.format_exc())
751
- msgBox = QtWidgets.QMessageBox()
752
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
753
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
754
- msgBox.setWindowTitle("Fatal Error")
755
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
756
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
757
- msgBox.exec()
758
-
759
- exit(-1)
760
 
761
  # generate function taken from Brian's code
762
  def generate(self,num_targets_per_gene, score_limit, space, output_file, fiveseq):
@@ -773,15 +516,12 @@ class genLibrary(QtWidgets.QMainWindow):
773
  endNum = endNum / 100
774
  checkStartandEndBool = True
775
  else:
776
- msgBox = QtWidgets.QMessageBox()
777
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
778
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
779
- msgBox.setWindowTitle("Invalid Targeting Range:")
780
- msgBox.setText(
781
- "Please select a targeting range between 0 and 100.")
782
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
783
- msgBox.exec()
784
-
785
  return -1
786
 
787
  for gene in self.gen_lib_dict:
@@ -908,29 +648,15 @@ class genLibrary(QtWidgets.QMainWindow):
908
 
909
  f.close()
910
  except PermissionError:
911
- msgBox = QtWidgets.QMessageBox()
912
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
913
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
914
- msgBox.setWindowTitle("File Cannot Open")
915
- msgBox.setText(
916
- "This file cannot be opened. Please make sure that the file is not opened elsewhere and try again.")
917
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Ok)
918
- msgBox.exec()
919
-
920
  return -1
921
  except Exception as e:
922
  print(e)
923
  return
924
  except Exception as e:
925
- logger.critical("Error in generate() in generate library.")
926
- logger.critical(e)
927
- logger.critical(traceback.format_exc())
928
- msgBox = QtWidgets.QMessageBox()
929
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
930
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
931
- msgBox.setWindowTitle("Fatal Error")
932
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
933
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
934
- msgBox.exec()
935
-
936
- exit(-1)
 
1
+ import models.GlobalSettings as GlobalSettings
2
  import os
3
  from PyQt5 import QtWidgets, Qt, uic, QtCore
4
  from functools import partial
5
+ from models.CSPRparser import CSPRparser
6
  import re
7
  import platform
8
  import traceback
9
  import math
10
+ from utils.ui import show_message, show_error, scale_ui, center_ui
11
+ from views.annotation_functions import *
12
 
 
13
  logger = GlobalSettings.logger
14
 
 
15
  # this class is a window that allows the user to select the settings for Generate Library
16
  # When the user clicks Generate Library, it goes ahead and gets the Annotation Data needed
17
  # Then the user can select the settings they want, and then hit submit.
18
  # It creates a txt file with the data
19
  class genLibrary(QtWidgets.QMainWindow):
 
20
  def __init__(self):
21
  try:
 
22
  super(genLibrary, self).__init__()
23
+ uic.loadUi(GlobalSettings.appdir + 'ui/generate_library.ui', self)
24
  self.setWindowTitle('Generate Library')
25
  self.setWindowIcon(Qt.QIcon(GlobalSettings.appdir + 'cas9image.ico'))
26
 
 
37
  self.Step3.setStyleSheet(groupbox_style.replace("Step1", "Step3"))
38
  self.Step4.setStyleSheet(groupbox_style.replace("Step1", "Step4"))
39
 
 
 
40
  self.cancel_button.clicked.connect(self.cancel_function)
41
  self.BrowseButton.clicked.connect(self.browse_function)
42
  self.submit_button.clicked.connect(self.submit_data)
43
  self.progressBar.setValue(0)
44
 
 
45
  self.anno_data = dict()
46
  self.kegg_nonKegg = ''
47
  self.gen_lib_dict = dict()
 
59
  # set the numbers for the minOn combo box
60
  for i in range(19, 70):
61
  self.minON_comboBox.addItem(str(i + 1))
62
+
63
+ scale_ui(self, custom_scale_width=950, custom_scale_height=500)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
 
65
  except Exception as e:
66
+ show_error("Error initializing generate library class.", e)
67
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
  # this function launches the window
69
  # Parameters:
70
  # annotation_data: a dictionary that has the data for the annotations searched for
 
80
  self.process = QtCore.QProcess()
81
  self.parser.fileName = org_file
82
 
 
83
  # setting the path and file name fields
84
  index1 = self.cspr_file.find('.')
85
  if platform.system() == "Windows":
 
102
  self.cspr_data = self.parser.gen_lib_parser(self.gen_lib_dict, GlobalSettings.mainWindow.endoChoice.currentText())
103
  self.get_endo_data()
104
 
105
+ center_ui(self)
 
106
  self.show()
107
  self.activateWindow()
108
  except Exception as e:
109
+ show_error("Error in launch() in generate library.", e)
 
 
 
 
 
 
 
 
 
 
 
110
 
111
  def get_endo_data(self):
112
  try:
 
133
  break
134
  f.close()
135
  except Exception as e:
136
+ show_error("Error in get_endo_data() in generate library.", e)
137
+
 
 
 
 
 
 
 
 
 
 
 
138
  # this is here in case the user clicks 'x' instead of cancel. Just calls the cancel function
139
  def closeEvent(self, event):
140
  try:
 
146
  else:
147
  event.accept()
148
  except Exception as e:
149
+ show_error("Error in closeEvent() in generate library.", e)
150
+
 
 
 
 
 
 
 
 
 
 
 
151
  # this function takes all of the cspr data and compresses it again for off-target usage
152
  def compress_file_off(self):
153
  try:
 
167
  f.write(output + '\n')
168
  f.close()
169
  except Exception as e:
170
+ show_error("Error in compress_file_off() in generate library.", e)
171
+
 
 
 
 
 
 
 
 
 
 
 
172
  # this function parses the temp_off file, which holds the off-target analysis results
173
  # it also updates each target in the cspr_data dictionary to replace the endo with the target's results in off-target
174
  def parse_off_file(self):
 
196
  tempTuple = (self.cspr_data[gene][i][0], self.cspr_data[gene][i][1], self.cspr_data[gene][i][2], self.cspr_data[gene][i][3], self.cspr_data[gene][i][4], scoreDict[self.cspr_data[gene][i][1]])
197
  self.cspr_data[gene][i] = tempTuple
198
  except Exception as e:
199
+ show_error("Error in parse_off_file() in generate library.", e)
200
+
 
 
 
 
 
 
 
 
 
 
 
201
  # this function runs the off_target command
202
  # NOTE: some changes may be needed to get it to work with other OS besides windows
203
  def get_offTarget_data(self, num_targets, minScore, spaceValue, output_file, fiveseq):
 
216
  #self.process.kill()
217
  if did_work != -1:
218
  self.cancel_function()
219
+ show_message(
220
+ fontSize=12,
221
+ icon=QtWidgets.QMessageBox.Icon.Information,
222
+ title="Library Generated!",
223
+ message="CASPER has finished generating your library!"
224
+ )
 
 
 
225
  os.remove(GlobalSettings.CSPR_DB + '/off_input.txt')
226
  os.remove(GlobalSettings.CSPR_DB + '/temp_off.txt')
227
 
 
301
  QtCore.QTimer.singleShot(100, partial(self.process.start, cmd))
302
  self.process.finished.connect(finished)
303
  except Exception as e:
304
+ show_error("Error in get_offTarget_data() in generate library.", e)
305
+
 
 
 
 
 
 
 
 
 
 
 
306
  # submit function
307
  # this function takes all of the input from the window, and calls the generate function
308
  # Still need to add the checks for 5' seq, and the percentage thing
 
329
  elif self.space_line_edit.text().isdigit():
330
  spaceValue = int(self.space_line_edit.text())
331
  elif not self.space_line_edit.text().isdigit():
332
+ show_message(
333
+ fontSize=12,
334
+ icon=QtWidgets.QMessageBox.Icon.Critical,
335
+ title="Error",
336
+ message="Please enter integers only for space between guides."
337
+ )
 
 
 
338
  return
339
  # if space value is more than 200, default to 200
340
  if spaceValue > 200:
341
  spaceValue = 200
342
  elif spaceValue < 0:
343
+ show_message(
344
+ fontSize=12,
345
+ icon=QtWidgets.QMessageBox.Icon.Critical,
346
+ title="Error",
347
+ message="Please enter a space-value that is 0 or greater."
348
+ )
 
 
 
349
  return
350
 
351
  if self.find_off_Checkbox.isChecked():
352
  self.compress_file_off()
353
 
 
 
354
  # get the fiveprimseq data and error check it
355
  if self.fiveprimeseq.text() != '' and self.fiveprimeseq.text().isalpha():
356
  fiveseq = self.fiveprimeseq.text()
357
  elif self.fiveprimeseq.text() != '' and not self.fiveprimeseq.text().isalpha():
358
+ show_message(
359
+ fontSize=12,
360
+ icon=QtWidgets.QMessageBox.Icon.Critical,
361
+ title="Error",
362
+ message="Please make sure only the letters A, T, G, or C are added into 5' End specificity box."
363
+ )
 
 
 
364
  return
365
 
 
366
  # get the targeting range data, and error check it here
367
  if not self.start_target_range.text().isdigit() or not self.end_target_range.text().isdigit():
368
+ show_message(
369
+ fontSize=12,
370
+ icon=QtWidgets.QMessageBox.Icon.Critical,
371
+ title="Error",
372
+ message="Error: Please make sure that the start and end target ranges are numbers only. Please make sure that start is 0 or greater, and end is 100 or less. "
373
+ )
 
 
 
374
  return
375
  elif int(self.start_target_range.text()) >= int(self.end_target_range.text()):
376
+ show_message(
377
+ fontSize=12,
378
+ icon=QtWidgets.QMessageBox.Icon.Critical,
379
+ title="Error",
380
+ message="Please make sure that the start number is always less than the end number"
381
+ )
 
 
 
382
  return
383
 
 
384
  # if they check Off-Targeting
385
  if self.find_off_Checkbox.isChecked():
386
  # make sure its a digit
387
  if self.maxOFF_comboBox.text() == '' or not self.maxOFF_comboBox.text().isdigit() and '.' not in self.maxOFF_comboBox.text():
388
+ show_message(
389
+ fontSize=12,
390
+ icon=QtWidgets.QMessageBox.Icon.Critical,
391
+ title="Error",
392
+ message="Please enter only numbers for Maximum Off-Target Score. It cannot be left blank"
393
+ )
 
 
 
394
  return
395
  else:
396
  # make sure it between 0 and .5
397
  if not 0.0 < float(self.maxOFF_comboBox.text()) <= .5:
398
+ show_message(
399
+ fontSize=12,
400
+ icon=QtWidgets.QMessageBox.Icon.Critical,
401
+ title="Error",
402
+ message="Please enter a max off-target score between 0 and 0.5!"
403
+ )
 
 
 
404
  return
405
  # compress the data, and then run off-targeting
406
  self.compress_file_off()
 
411
 
412
  if did_work != -1:
413
  self.cancel_function()
414
+ show_message(
415
+ fontSize=12,
416
+ icon=QtWidgets.QMessageBox.Icon.Critical,
417
+ title="Library Generated!",
418
+ message="CASPER has finished generating your library!"
419
+ )
 
 
 
420
  except Exception as e:
421
+ show_error("Error in submit_data() in generate library.", e)
422
+
 
 
 
 
 
 
 
 
 
 
 
 
423
  # clears everything and hides the window
424
  def cancel_function(self):
425
  try:
426
  if self.off_target_running:
427
  msgBox = QtWidgets.QMessageBox()
428
+ msgBox.setStyleSheet("font: " + str(12) + "pt 'Arial'")
429
  msgBox.setIcon(QtWidgets.QMessageBox.Icon.Question)
430
  msgBox.setWindowTitle("Off-Targeting is running")
431
  msgBox.setText(
 
462
 
463
  self.hide()
464
  except Exception as e:
465
+ show_error("Error in cancel_function() in generate library.", e)
466
+
 
 
 
 
 
 
 
 
 
 
 
 
467
  # allows the user to browse for a folder
468
  # stores their selection in the output_path line edit
469
  def browse_function(self):
 
483
  else:
484
  self.output_path.setText(mydir + "/")
485
  except Exception as e:
486
+ show_error("Error in browse_function() in generate library.", e)
 
 
 
 
 
 
 
 
 
 
 
487
 
488
  # this function builds the dictionary that is used in the generate function
489
  # this is the version that builds it from data from feature_table, gbff, or gff
 
499
  ### Order: chromosome number, gene start, gene end, dir of gene, gene description, gene name/locus tag
500
  self.gen_lib_dict[feature_name] = [chrom,int(feature.location.start),int(feature.location.end),get_strand(feature),get_description(feature),get_name(feature)]
501
  except Exception as e:
502
+ show_error("Error in build_dict_non_kegg() in generate library.", e)
 
 
 
 
 
 
 
 
 
 
 
503
 
504
  # generate function taken from Brian's code
505
  def generate(self,num_targets_per_gene, score_limit, space, output_file, fiveseq):
 
516
  endNum = endNum / 100
517
  checkStartandEndBool = True
518
  else:
519
+ show_message(
520
+ fontSize=12,
521
+ icon=QtWidgets.QMessageBox.Icon.Critical,
522
+ title="Invalid Targeting Range:",
523
+ message="Please select a targeting range between 0 and 100."
524
+ )
 
 
 
525
  return -1
526
 
527
  for gene in self.gen_lib_dict:
 
648
 
649
  f.close()
650
  except PermissionError:
651
+ show_message(
652
+ fontSize=12,
653
+ icon=QtWidgets.QMessageBox.Icon.Critical,
654
+ title="File Cannot Open",
655
+ message="This file cannot be opened. Please make sure that the file is not opened elsewhere and try again."
656
+ )
 
 
 
657
  return -1
658
  except Exception as e:
659
  print(e)
660
  return
661
  except Exception as e:
662
+ show_error("Error in generate() in generate library.", e)
 
 
 
 
 
 
 
 
 
 
 
genomeBrowser.py → views/genomeBrowser.py RENAMED
@@ -1,5 +1,5 @@
1
  import sys, os
2
- import GlobalSettings
3
  from PyQt5 import QtWidgets, uic, QtGui, QtCore, Qt
4
  import PyQt5.QtWebEngineWidgets
5
  from PyQt5.QtWidgets import QApplication
@@ -12,8 +12,8 @@ import glob
12
  import ssl
13
  import traceback
14
  import webbrowser
 
15
 
16
- #global logger
17
  logger = GlobalSettings.logger
18
 
19
  ssl._create_default_https_context = ssl._create_unverified_context
@@ -22,7 +22,6 @@ class WebEnginePage(PyQt5.QtWebEngineWidgets.QWebEnginePage):
22
  def certificateError(self, certificateError):
23
  print("ssl error")
24
 
25
-
26
  class genomebrowser(QtWidgets.QWidget):
27
  def __init__(self, parent=None):
28
  try:
@@ -30,18 +29,8 @@ class genomebrowser(QtWidgets.QWidget):
30
  default_config.setProtocol(QtNetwork.QSsl.TlsV1_2)
31
  QtNetwork.QSslConfiguration.setDefaultConfiguration(default_config)
32
  except Exception as e:
33
- logger.critical("Error initializing genomebrowser class.")
34
- logger.critical(e)
35
- logger.critical(traceback.format_exc())
36
- msgBox = QtWidgets.QMessageBox()
37
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
38
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
39
- msgBox.setWindowTitle("Fatal Error")
40
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
41
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
42
- msgBox.exec()
43
- exit(-1)
44
-
45
  def splitStringNCBI(self, longString):
46
  try:
47
  return (longString).split(':')[2]
@@ -66,19 +55,8 @@ class genomebrowser(QtWidgets.QWidget):
66
  return genomeList
67
 
68
  except Exception as e:
69
- logger.critical("Error in ncbiAPI() in genomebrowser.")
70
- logger.critical(e)
71
- logger.critical(traceback.format_exc())
72
- msgBox = QtWidgets.QMessageBox()
73
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
74
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
75
- msgBox.setWindowTitle("Fatal Error")
76
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
77
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
78
- msgBox.exec()
79
-
80
- exit(-1)
81
-
82
  def createHtml(self, genomeList):
83
  try:
84
  htmlString1 = """
@@ -135,18 +113,8 @@ class genomebrowser(QtWidgets.QWidget):
135
 
136
  raw.write(htmlString3)
137
  except Exception as e:
138
- logger.critical("Error in createHtml() in genomebrowser.")
139
- logger.critical(e)
140
- logger.critical(traceback.format_exc())
141
- msgBox = QtWidgets.QMessageBox()
142
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
143
- msgBox.setWindowTitle("Fatal Error")
144
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
145
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
146
- msgBox.exec()
147
-
148
- exit(-1)
149
-
150
  def createGraph(self, p):
151
  try:
152
  selectedGenome = p.annotation_files.currentText()
@@ -183,16 +151,4 @@ class genomebrowser(QtWidgets.QWidget):
183
  ### Add logic for Linux ?
184
  webbrowser.open(file_path, new=2)
185
  except Exception as e:
186
- logger.critical("Error in createGraph() in genomebrowser.")
187
-
188
- logger.critical(e)
189
- logger.critical(traceback.format_exc())
190
- msgBox = QtWidgets.QMessageBox()
191
- msgBox.setStyleSheet("font: " + str(self.fontSize) + "pt 'Arial'")
192
- msgBox.setIcon(QtWidgets.QMessageBox.Icon.Critical)
193
- msgBox.setWindowTitle("Fatal Error")
194
- msgBox.setText("Fatal Error:\n"+str(e)+ "\n\nFor more information on this error, look at CASPER.log in the application folder.")
195
- msgBox.addButton(QtWidgets.QMessageBox.StandardButton.Close)
196
- msgBox.exec()
197
-
198
- exit(-1)
 
1
  import sys, os
2
+ import models.GlobalSettings as GlobalSettings
3
  from PyQt5 import QtWidgets, uic, QtGui, QtCore, Qt
4
  import PyQt5.QtWebEngineWidgets
5
  from PyQt5.QtWidgets import QApplication
 
12
  import ssl
13
  import traceback
14
  import webbrowser
15
+ from utils.ui import show_error
16
 
 
17
  logger = GlobalSettings.logger
18
 
19
  ssl._create_default_https_context = ssl._create_unverified_context
 
22
  def certificateError(self, certificateError):
23
  print("ssl error")
24
 
 
25
  class genomebrowser(QtWidgets.QWidget):
26
  def __init__(self, parent=None):
27
  try:
 
29
  default_config.setProtocol(QtNetwork.QSsl.TlsV1_2)
30
  QtNetwork.QSslConfiguration.setDefaultConfiguration(default_config)
31
  except Exception as e:
32
+ show_error("Error initializing genomebrowser class.", e)
33
+
 
 
 
 
 
 
 
 
 
 
34
  def splitStringNCBI(self, longString):
35
  try:
36
  return (longString).split(':')[2]
 
55
  return genomeList
56
 
57
  except Exception as e:
58
+ show_error("Error in ncbiAPI() in genomebrowser.", e)
59
+
 
 
 
 
 
 
 
 
 
 
 
60
  def createHtml(self, genomeList):
61
  try:
62
  htmlString1 = """
 
113
 
114
  raw.write(htmlString3)
115
  except Exception as e:
116
+ show_error("Error in createHtml() in genomebrowser.", e)
117
+
 
 
 
 
 
 
 
 
 
 
118
  def createGraph(self, p):
119
  try:
120
  selectedGenome = p.annotation_files.currentText()
 
151
  ### Add logic for Linux ?
152
  webbrowser.open(file_path, new=2)
153
  except Exception as e:
154
+ show_error("Error in createGraph() in genomebrowser.", e)